diff --git a/builder/azure/LICENSE b/builder/azure/LICENSE deleted file mode 100644 index 5ae193c94..000000000 --- a/builder/azure/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) Microsoft Corporation - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/builder/azure/TODO.md b/builder/azure/TODO.md deleted file mode 100644 index f35a4607b..000000000 --- a/builder/azure/TODO.md +++ /dev/null @@ -1,16 +0,0 @@ -Here's a list of things we like to get done in no particular order: - -- [ ] Blob/image copy post-processor -- [ ] Blob/image rename post-processor -- [ ] SSH to private ip through subnet -- [ ] chroot builder -- [ ] support cross-storage account image source (i.e. pre-build blob copy) -- [ ] look up object id when using device code (graph api /me ?) -- [ ] device flow support for Windows -- [x] look up tenant id in all cases (see device flow code) -- [ ] look up resource group of storage account -- [ ] include all _data_ disks in artifact too -- [ ] windows sysprep provisioner (since it seems to generate a certain issue volume) -- [ ] allow arbitrary json patching for deployment document -- [ ] tag all resources with user-supplied tag -- [ ] managed disk support diff --git a/builder/azure/arm/TestVirtualMachineDeployment05.approved.txt b/builder/azure/arm/TestVirtualMachineDeployment05.approved.txt deleted file mode 100644 index 31532ddbe..000000000 --- a/builder/azure/arm/TestVirtualMachineDeployment05.approved.txt +++ /dev/null @@ -1,118 +0,0 @@ -{ - "$schema": "http://schema.management.azure.com/schemas/2014-04-01-preview/deploymentTemplate.json", - "contentVersion": "1.0.0.0", - "parameters": { - "adminPassword": { - "type": "string" - }, - "adminUsername": { - "type": "string" - }, - "dnsNameForPublicIP": { - "type": "string" - }, - "osDiskName": { - "type": "string" - }, - "storageAccountBlobEndpoint": { - "type": "string" - }, - "vmName": { - "type": "string" - }, - "vmSize": { - "type": "string" - } - }, - "resources": [ - { - "apiVersion": "[variables('apiVersion')]", - "dependsOn": [], - "location": "[variables('location')]", - "name": "[variables('nicName')]", - "properties": { - "ipConfigurations": [ - { - "name": "ipconfig", - "properties": { - "privateIPAllocationMethod": "Dynamic", - "subnet": { - "id": "[variables('subnetRef')]" - } - } - } - ] - }, - "type": "Microsoft.Network/networkInterfaces" - }, - { - "apiVersion": "[variables('apiVersion')]", - "dependsOn": [ - "[concat('Microsoft.Network/networkInterfaces/', variables('nicName'))]" - ], - "location": "[variables('location')]", - "name": "[parameters('vmName')]", - "properties": { - "diagnosticsProfile": { - "bootDiagnostics": { - "enabled": false - } - }, - "hardwareProfile": { - "vmSize": "[parameters('vmSize')]" - }, - "networkProfile": { - "networkInterfaces": [ - { - "id": "[resourceId('Microsoft.Network/networkInterfaces', variables('nicName'))]" - } - ] - }, - "osProfile": { - "adminPassword": "[parameters('adminPassword')]", - "adminUsername": "[parameters('adminUsername')]", - "computerName": "[parameters('vmName')]", - "linuxConfiguration": { - "ssh": { - "publicKeys": [ - { - "keyData": "", - "path": "[variables('sshKeyPath')]" - } - ] - } - } - }, - "storageProfile": { - "osDisk": { - "caching": "ReadWrite", - "createOption": "FromImage", - "image": { - "uri": "https://localhost/custom.vhd" - }, - "name": "[parameters('osDiskName')]", - "osType": "Linux", - "vhd": { - "uri": "[concat(parameters('storageAccountBlobEndpoint'),variables('vmStorageAccountContainerName'),'/', parameters('osDiskName'),'.vhd')]" - } - } - } - }, - "type": "Microsoft.Compute/virtualMachines" - } - ], - "variables": { - "addressPrefix": "10.0.0.0/16", - "apiVersion": "2015-06-15", - "location": "[resourceGroup().location]", - "publicIPAddressType": "Dynamic", - "sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]", - "subnetAddressPrefix": "10.0.0.0/24", - "subnetName": "ignore", - "subnetRef": "[concat(variables('vnetID'),'/subnets/',variables('subnetName'))]", - "virtualNetworkName": "ignore", - "virtualNetworkResourceGroup": "ignore", - "vmStorageAccountContainerName": "images", - "vnetID": "[resourceId(variables('virtualNetworkResourceGroup'), 'Microsoft.Network/virtualNetworks', variables('virtualNetworkName'))]" - } -} \ No newline at end of file diff --git a/builder/azure/arm/artifact.go b/builder/azure/arm/artifact.go deleted file mode 100644 index 6a3463ec3..000000000 --- a/builder/azure/arm/artifact.go +++ /dev/null @@ -1,261 +0,0 @@ -package arm - -import ( - "bytes" - "fmt" - "log" - "net/url" - "path" - "strings" -) - -const ( - BuilderId = "Azure.ResourceManagement.VMImage" -) - -type AdditionalDiskArtifact struct { - AdditionalDiskUri string - AdditionalDiskUriReadOnlySas string -} - -type Artifact struct { - // OS type: Linux, Windows - OSType string - - // VHD - StorageAccountLocation string - OSDiskUri string - TemplateUri string - OSDiskUriReadOnlySas string - TemplateUriReadOnlySas string - - // Managed Image - ManagedImageResourceGroupName string - ManagedImageName string - ManagedImageLocation string - ManagedImageId string - ManagedImageOSDiskSnapshotName string - ManagedImageDataDiskSnapshotPrefix string - // ARM resource id for Shared Image Gallery - ManagedImageSharedImageGalleryId string - - // Additional Disks - AdditionalDisks *[]AdditionalDiskArtifact - - // StateData should store data such as GeneratedData - // to be shared with post-processors - StateData map[string]interface{} -} - -func NewManagedImageArtifact(osType, resourceGroup, name, location, id, osDiskSnapshotName, dataDiskSnapshotPrefix string, generatedData map[string]interface{}, keepOSDisk bool, template *CaptureTemplate, getSasUrl func(name string) string) (*Artifact, error) { - res := Artifact{ - ManagedImageResourceGroupName: resourceGroup, - ManagedImageName: name, - ManagedImageLocation: location, - ManagedImageId: id, - OSType: osType, - ManagedImageOSDiskSnapshotName: osDiskSnapshotName, - ManagedImageDataDiskSnapshotPrefix: dataDiskSnapshotPrefix, - StateData: generatedData, - } - - if keepOSDisk { - if template == nil { - log.Printf("artifact error: nil capture template") - return &res, nil - } - - if len(template.Resources) != 1 { - log.Printf("artifact error: malformed capture template, expected one resource") - return &res, nil - } - - vhdUri, err := url.Parse(template.Resources[0].Properties.StorageProfile.OSDisk.Image.Uri) - if err != nil { - log.Printf("artifact error: Error parsing osdisk url: %s", err) - return &res, nil - } - - res.OSDiskUri = vhdUri.String() - res.OSDiskUriReadOnlySas = getSasUrl(getStorageUrlPath(vhdUri)) - } - - return &res, nil -} - -func NewManagedImageArtifactWithSIGAsDestination(osType, resourceGroup, name, location, id, osDiskSnapshotName, dataDiskSnapshotPrefix, destinationSharedImageGalleryId string, generatedData map[string]interface{}) (*Artifact, error) { - return &Artifact{ - ManagedImageResourceGroupName: resourceGroup, - ManagedImageName: name, - ManagedImageLocation: location, - ManagedImageId: id, - OSType: osType, - ManagedImageOSDiskSnapshotName: osDiskSnapshotName, - ManagedImageDataDiskSnapshotPrefix: dataDiskSnapshotPrefix, - ManagedImageSharedImageGalleryId: destinationSharedImageGalleryId, - StateData: generatedData, - }, nil -} - -func NewArtifact(template *CaptureTemplate, getSasUrl func(name string) string, osType string, generatedData map[string]interface{}) (*Artifact, error) { - if template == nil { - return nil, fmt.Errorf("nil capture template") - } - - if len(template.Resources) != 1 { - return nil, fmt.Errorf("malformed capture template, expected one resource") - } - - vhdUri, err := url.Parse(template.Resources[0].Properties.StorageProfile.OSDisk.Image.Uri) - if err != nil { - return nil, err - } - - templateUri, err := storageUriToTemplateUri(vhdUri) - if err != nil { - return nil, err - } - - var additional_disks *[]AdditionalDiskArtifact - if template.Resources[0].Properties.StorageProfile.DataDisks != nil { - data_disks := make([]AdditionalDiskArtifact, len(template.Resources[0].Properties.StorageProfile.DataDisks)) - for i, additionaldisk := range template.Resources[0].Properties.StorageProfile.DataDisks { - additionalVhdUri, err := url.Parse(additionaldisk.Image.Uri) - if err != nil { - return nil, err - } - data_disks[i].AdditionalDiskUri = additionalVhdUri.String() - data_disks[i].AdditionalDiskUriReadOnlySas = getSasUrl(getStorageUrlPath(additionalVhdUri)) - } - additional_disks = &data_disks - } - - return &Artifact{ - OSType: osType, - OSDiskUri: vhdUri.String(), - OSDiskUriReadOnlySas: getSasUrl(getStorageUrlPath(vhdUri)), - TemplateUri: templateUri.String(), - TemplateUriReadOnlySas: getSasUrl(getStorageUrlPath(templateUri)), - - AdditionalDisks: additional_disks, - - StorageAccountLocation: template.Resources[0].Location, - - StateData: generatedData, - }, nil -} - -func getStorageUrlPath(u *url.URL) string { - parts := strings.Split(u.Path, "/") - return strings.Join(parts[3:], "/") -} - -func storageUriToTemplateUri(su *url.URL) (*url.URL, error) { - // packer-osDisk.4085bb15-3644-4641-b9cd-f575918640b4.vhd -> 4085bb15-3644-4641-b9cd-f575918640b4 - filename := path.Base(su.Path) - parts := strings.Split(filename, ".") - - if len(parts) < 3 { - return nil, fmt.Errorf("malformed URL") - } - - // packer-osDisk.4085bb15-3644-4641-b9cd-f575918640b4.vhd -> packer - prefixParts := strings.Split(parts[0], "-") - prefix := strings.Join(prefixParts[:len(prefixParts)-1], "-") - - templateFilename := fmt.Sprintf("%s-vmTemplate.%s.json", prefix, parts[1]) - - // https://storage.blob.core.windows.net/system/Microsoft.Compute/Images/images/packer-osDisk.4085bb15-3644-4641-b9cd-f575918640b4.vhd" - // -> - // https://storage.blob.core.windows.net/system/Microsoft.Compute/Images/images/packer-vmTemplate.4085bb15-3644-4641-b9cd-f575918640b4.json" - return url.Parse(strings.Replace(su.String(), filename, templateFilename, 1)) -} - -func (a *Artifact) isManagedImage() bool { - return a.ManagedImageResourceGroupName != "" -} - -func (*Artifact) BuilderId() string { - return BuilderId -} - -func (*Artifact) Files() []string { - return []string{} -} - -func (a *Artifact) Id() string { - if a.OSDiskUri != "" { - return a.OSDiskUri - } - return a.ManagedImageId -} - -func (a *Artifact) State(name string) interface{} { - if _, ok := a.StateData[name]; ok { - return a.StateData[name] - } - - switch name { - case "atlas.artifact.metadata": - return a.stateAtlasMetadata() - default: - return nil - } -} - -func (a *Artifact) String() string { - var buf bytes.Buffer - - buf.WriteString(fmt.Sprintf("%s:\n\n", a.BuilderId())) - buf.WriteString(fmt.Sprintf("OSType: %s\n", a.OSType)) - if a.isManagedImage() { - buf.WriteString(fmt.Sprintf("ManagedImageResourceGroupName: %s\n", a.ManagedImageResourceGroupName)) - buf.WriteString(fmt.Sprintf("ManagedImageName: %s\n", a.ManagedImageName)) - buf.WriteString(fmt.Sprintf("ManagedImageId: %s\n", a.ManagedImageId)) - buf.WriteString(fmt.Sprintf("ManagedImageLocation: %s\n", a.ManagedImageLocation)) - if a.ManagedImageOSDiskSnapshotName != "" { - buf.WriteString(fmt.Sprintf("ManagedImageOSDiskSnapshotName: %s\n", a.ManagedImageOSDiskSnapshotName)) - } - if a.ManagedImageDataDiskSnapshotPrefix != "" { - buf.WriteString(fmt.Sprintf("ManagedImageDataDiskSnapshotPrefix: %s\n", a.ManagedImageDataDiskSnapshotPrefix)) - } - if a.ManagedImageSharedImageGalleryId != "" { - buf.WriteString(fmt.Sprintf("ManagedImageSharedImageGalleryId: %s\n", a.ManagedImageSharedImageGalleryId)) - } - if a.OSDiskUri != "" { - buf.WriteString(fmt.Sprintf("OSDiskUri: %s\n", a.OSDiskUri)) - } - if a.OSDiskUriReadOnlySas != "" { - buf.WriteString(fmt.Sprintf("OSDiskUriReadOnlySas: %s\n", a.OSDiskUriReadOnlySas)) - } - } else { - buf.WriteString(fmt.Sprintf("StorageAccountLocation: %s\n", a.StorageAccountLocation)) - buf.WriteString(fmt.Sprintf("OSDiskUri: %s\n", a.OSDiskUri)) - buf.WriteString(fmt.Sprintf("OSDiskUriReadOnlySas: %s\n", a.OSDiskUriReadOnlySas)) - buf.WriteString(fmt.Sprintf("TemplateUri: %s\n", a.TemplateUri)) - buf.WriteString(fmt.Sprintf("TemplateUriReadOnlySas: %s\n", a.TemplateUriReadOnlySas)) - if a.AdditionalDisks != nil { - for i, additionaldisk := range *a.AdditionalDisks { - buf.WriteString(fmt.Sprintf("AdditionalDiskUri (datadisk-%d): %s\n", i+1, additionaldisk.AdditionalDiskUri)) - buf.WriteString(fmt.Sprintf("AdditionalDiskUriReadOnlySas (datadisk-%d): %s\n", i+1, additionaldisk.AdditionalDiskUriReadOnlySas)) - } - } - } - - return buf.String() -} - -func (*Artifact) Destroy() error { - return nil -} - -func (a *Artifact) stateAtlasMetadata() interface{} { - metadata := make(map[string]string) - metadata["StorageAccountLocation"] = a.StorageAccountLocation - metadata["OSDiskUri"] = a.OSDiskUri - metadata["OSDiskUriReadOnlySas"] = a.OSDiskUriReadOnlySas - metadata["TemplateUri"] = a.TemplateUri - metadata["TemplateUriReadOnlySas"] = a.TemplateUriReadOnlySas - - return metadata -} diff --git a/builder/azure/arm/artifact_test.go b/builder/azure/arm/artifact_test.go deleted file mode 100644 index 81acdb101..000000000 --- a/builder/azure/arm/artifact_test.go +++ /dev/null @@ -1,521 +0,0 @@ -package arm - -import ( - "fmt" - "strings" - "testing" -) - -func getFakeSasUrl(name string) string { - return fmt.Sprintf("SAS-%s", name) -} - -func generatedData() map[string]interface{} { - return make(map[string]interface{}) -} - -func TestArtifactIdVHD(t *testing.T) { - template := CaptureTemplate{ - Resources: []CaptureResources{ - { - Properties: CaptureProperties{ - StorageProfile: CaptureStorageProfile{ - OSDisk: CaptureDisk{ - Image: CaptureUri{ - Uri: "https://storage.blob.core.windows.net/system/Microsoft.Compute/Images/images/packer-osDisk.4085bb15-3644-4641-b9cd-f575918640b4.vhd", - }, - }, - }, - }, - Location: "southcentralus", - }, - }, - } - - artifact, err := NewArtifact(&template, getFakeSasUrl, "Linux", generatedData()) - if err != nil { - t.Fatalf("err=%s", err) - } - - expected := "https://storage.blob.core.windows.net/system/Microsoft.Compute/Images/images/packer-osDisk.4085bb15-3644-4641-b9cd-f575918640b4.vhd" - - result := artifact.Id() - if result != expected { - t.Fatalf("bad: %s", result) - } -} - -func TestArtifactIDManagedImage(t *testing.T) { - template := CaptureTemplate{ - Resources: []CaptureResources{ - { - Properties: CaptureProperties{ - StorageProfile: CaptureStorageProfile{ - OSDisk: CaptureDisk{ - Image: CaptureUri{ - Uri: "https://storage.blob.core.windows.net/system/Microsoft.Compute/Images/images/packer-osDisk.4085bb15-3644-4641-b9cd-f575918640b4.vhd", - }, - }, - }, - }, - Location: "southcentralus", - }, - }, - } - - artifact, err := NewManagedImageArtifact("Linux", "fakeResourceGroup", "fakeName", "fakeLocation", "fakeID", "fakeOsDiskSnapshotName", "fakeDataDiskSnapshotPrefix", generatedData(), false, &template, getFakeSasUrl) - if err != nil { - t.Fatalf("err=%s", err) - } - - expected := `Azure.ResourceManagement.VMImage: - -OSType: Linux -ManagedImageResourceGroupName: fakeResourceGroup -ManagedImageName: fakeName -ManagedImageId: fakeID -ManagedImageLocation: fakeLocation -ManagedImageOSDiskSnapshotName: fakeOsDiskSnapshotName -ManagedImageDataDiskSnapshotPrefix: fakeDataDiskSnapshotPrefix -` - - result := artifact.String() - if result != expected { - t.Fatalf("bad: %s", result) - } -} - -func TestArtifactIDManagedImageWithoutOSDiskSnapshotName(t *testing.T) { - template := CaptureTemplate{ - Resources: []CaptureResources{ - { - Properties: CaptureProperties{ - StorageProfile: CaptureStorageProfile{ - OSDisk: CaptureDisk{ - Image: CaptureUri{ - Uri: "https://storage.blob.core.windows.net/system/Microsoft.Compute/Images/images/packer-osDisk.4085bb15-3644-4641-b9cd-f575918640b4.vhd", - }, - }, - }, - }, - Location: "southcentralus", - }, - }, - } - - artifact, err := NewManagedImageArtifact("Linux", "fakeResourceGroup", "fakeName", "fakeLocation", "fakeID", "", "fakeDataDiskSnapshotPrefix", generatedData(), false, &template, getFakeSasUrl) - if err != nil { - t.Fatalf("err=%s", err) - } - - expected := `Azure.ResourceManagement.VMImage: - -OSType: Linux -ManagedImageResourceGroupName: fakeResourceGroup -ManagedImageName: fakeName -ManagedImageId: fakeID -ManagedImageLocation: fakeLocation -ManagedImageDataDiskSnapshotPrefix: fakeDataDiskSnapshotPrefix -` - - result := artifact.String() - if result != expected { - t.Fatalf("bad: %s", result) - } -} - -func TestArtifactIDManagedImageWithoutDataDiskSnapshotPrefix(t *testing.T) { - template := CaptureTemplate{ - Resources: []CaptureResources{ - { - Properties: CaptureProperties{ - StorageProfile: CaptureStorageProfile{ - OSDisk: CaptureDisk{ - Image: CaptureUri{ - Uri: "https://storage.blob.core.windows.net/system/Microsoft.Compute/Images/images/packer-osDisk.4085bb15-3644-4641-b9cd-f575918640b4.vhd", - }, - }, - }, - }, - Location: "southcentralus", - }, - }, - } - - artifact, err := NewManagedImageArtifact("Linux", "fakeResourceGroup", "fakeName", "fakeLocation", "fakeID", "fakeOsDiskSnapshotName", "", generatedData(), false, &template, getFakeSasUrl) - if err != nil { - t.Fatalf("err=%s", err) - } - - expected := `Azure.ResourceManagement.VMImage: - -OSType: Linux -ManagedImageResourceGroupName: fakeResourceGroup -ManagedImageName: fakeName -ManagedImageId: fakeID -ManagedImageLocation: fakeLocation -ManagedImageOSDiskSnapshotName: fakeOsDiskSnapshotName -` - - result := artifact.String() - if result != expected { - t.Fatalf("bad: %s", result) - } -} - -func TestArtifactIDManagedImageWithKeepingTheOSDisk(t *testing.T) { - template := CaptureTemplate{ - Resources: []CaptureResources{ - { - Properties: CaptureProperties{ - StorageProfile: CaptureStorageProfile{ - OSDisk: CaptureDisk{ - Image: CaptureUri{ - Uri: "https://storage.blob.core.windows.net/system/Microsoft.Compute/Images/images/packer-osDisk.4085bb15-3644-4641-b9cd-f575918640b4.vhd", - }, - }, - }, - }, - Location: "southcentralus", - }, - }, - } - - artifact, err := NewManagedImageArtifact("Linux", "fakeResourceGroup", "fakeName", "fakeLocation", "fakeID", "fakeOsDiskSnapshotName", "", generatedData(), true, &template, getFakeSasUrl) - if err != nil { - t.Fatalf("err=%s", err) - } - - expected := `Azure.ResourceManagement.VMImage: - -OSType: Linux -ManagedImageResourceGroupName: fakeResourceGroup -ManagedImageName: fakeName -ManagedImageId: fakeID -ManagedImageLocation: fakeLocation -ManagedImageOSDiskSnapshotName: fakeOsDiskSnapshotName -OSDiskUri: https://storage.blob.core.windows.net/system/Microsoft.Compute/Images/images/packer-osDisk.4085bb15-3644-4641-b9cd-f575918640b4.vhd -OSDiskUriReadOnlySas: SAS-Images/images/packer-osDisk.4085bb15-3644-4641-b9cd-f575918640b4.vhd -` - - result := artifact.String() - if result != expected { - t.Fatalf("bad: %s", result) - } -} - -func TestArtifactIDManagedImageWithSharedImageGalleryId(t *testing.T) { - artifact, err := NewManagedImageArtifactWithSIGAsDestination("Linux", "fakeResourceGroup", "fakeName", "fakeLocation", "fakeID", "fakeOsDiskSnapshotName", "fakeDataDiskSnapshotPrefix", "fakeSharedImageGallery", generatedData()) - if err != nil { - t.Fatalf("err=%s", err) - } - - expected := `Azure.ResourceManagement.VMImage: - -OSType: Linux -ManagedImageResourceGroupName: fakeResourceGroup -ManagedImageName: fakeName -ManagedImageId: fakeID -ManagedImageLocation: fakeLocation -ManagedImageOSDiskSnapshotName: fakeOsDiskSnapshotName -ManagedImageDataDiskSnapshotPrefix: fakeDataDiskSnapshotPrefix -ManagedImageSharedImageGalleryId: fakeSharedImageGallery -` - - result := artifact.String() - if result != expected { - t.Fatalf("bad: %s", result) - } -} - -func TestArtifactString(t *testing.T) { - template := CaptureTemplate{ - Resources: []CaptureResources{ - { - Properties: CaptureProperties{ - StorageProfile: CaptureStorageProfile{ - OSDisk: CaptureDisk{ - Image: CaptureUri{ - Uri: "https://storage.blob.core.windows.net/system/Microsoft.Compute/Images/images/packer-osDisk.4085bb15-3644-4641-b9cd-f575918640b4.vhd", - }, - }, - }, - }, - Location: "southcentralus", - }, - }, - } - - artifact, err := NewArtifact(&template, getFakeSasUrl, "Linux", generatedData()) - if err != nil { - t.Fatalf("err=%s", err) - } - - testSubject := artifact.String() - if !strings.Contains(testSubject, "OSDiskUri: https://storage.blob.core.windows.net/system/Microsoft.Compute/Images/images/packer-osDisk.4085bb15-3644-4641-b9cd-f575918640b4.vhd") { - t.Errorf("Expected String() output to contain OSDiskUri") - } - if !strings.Contains(testSubject, "OSDiskUriReadOnlySas: SAS-Images/images/packer-osDisk.4085bb15-3644-4641-b9cd-f575918640b4.vhd") { - t.Errorf("Expected String() output to contain OSDiskUriReadOnlySas") - } - if !strings.Contains(testSubject, "TemplateUri: https://storage.blob.core.windows.net/system/Microsoft.Compute/Images/images/packer-vmTemplate.4085bb15-3644-4641-b9cd-f575918640b4.json") { - t.Errorf("Expected String() output to contain TemplateUri") - } - if !strings.Contains(testSubject, "TemplateUriReadOnlySas: SAS-Images/images/packer-vmTemplate.4085bb15-3644-4641-b9cd-f575918640b4.json") { - t.Errorf("Expected String() output to contain TemplateUriReadOnlySas") - } - if !strings.Contains(testSubject, "StorageAccountLocation: southcentralus") { - t.Errorf("Expected String() output to contain StorageAccountLocation") - } - if !strings.Contains(testSubject, "OSType: Linux") { - t.Errorf("Expected String() output to contain OSType") - } -} - -func TestAdditionalDiskArtifactString(t *testing.T) { - template := CaptureTemplate{ - Resources: []CaptureResources{ - { - Properties: CaptureProperties{ - StorageProfile: CaptureStorageProfile{ - OSDisk: CaptureDisk{ - Image: CaptureUri{ - Uri: "https://storage.blob.core.windows.net/system/Microsoft.Compute/Images/images/packer-osDisk.4085bb15-3644-4641-b9cd-f575918640b4.vhd", - }, - }, - DataDisks: []CaptureDisk{ - { - Image: CaptureUri{ - Uri: "https://storage.blob.core.windows.net/system/Microsoft.Compute/Images/images/packer-datadisk-1.4085bb15-3644-4641-b9cd-f575918640b4.vhd", - }, - }, - }, - }, - }, - Location: "southcentralus", - }, - }, - } - - artifact, err := NewArtifact(&template, getFakeSasUrl, "Linux", generatedData()) - if err != nil { - t.Fatalf("err=%s", err) - } - - testSubject := artifact.String() - if !strings.Contains(testSubject, "OSDiskUri: https://storage.blob.core.windows.net/system/Microsoft.Compute/Images/images/packer-osDisk.4085bb15-3644-4641-b9cd-f575918640b4.vhd") { - t.Errorf("Expected String() output to contain OSDiskUri") - } - if !strings.Contains(testSubject, "OSDiskUriReadOnlySas: SAS-Images/images/packer-osDisk.4085bb15-3644-4641-b9cd-f575918640b4.vhd") { - t.Errorf("Expected String() output to contain OSDiskUriReadOnlySas") - } - if !strings.Contains(testSubject, "TemplateUri: https://storage.blob.core.windows.net/system/Microsoft.Compute/Images/images/packer-vmTemplate.4085bb15-3644-4641-b9cd-f575918640b4.json") { - t.Errorf("Expected String() output to contain TemplateUri") - } - if !strings.Contains(testSubject, "TemplateUriReadOnlySas: SAS-Images/images/packer-vmTemplate.4085bb15-3644-4641-b9cd-f575918640b4.json") { - t.Errorf("Expected String() output to contain TemplateUriReadOnlySas") - } - if !strings.Contains(testSubject, "StorageAccountLocation: southcentralus") { - t.Errorf("Expected String() output to contain StorageAccountLocation") - } - if !strings.Contains(testSubject, "OSType: Linux") { - t.Errorf("Expected String() output to contain OSType") - } - if !strings.Contains(testSubject, "AdditionalDiskUri (datadisk-1): https://storage.blob.core.windows.net/system/Microsoft.Compute/Images/images/packer-datadisk-1.4085bb15-3644-4641-b9cd-f575918640b4.vhd") { - t.Errorf("Expected String() output to contain AdditionalDiskUri") - } - if !strings.Contains(testSubject, "AdditionalDiskUriReadOnlySas (datadisk-1): SAS-Images/images/packer-datadisk-1.4085bb15-3644-4641-b9cd-f575918640b4.vhd") { - t.Errorf("Expected String() output to contain AdditionalDiskUriReadOnlySas") - } -} - -func TestArtifactProperties(t *testing.T) { - template := CaptureTemplate{ - Resources: []CaptureResources{ - { - Properties: CaptureProperties{ - StorageProfile: CaptureStorageProfile{ - OSDisk: CaptureDisk{ - Image: CaptureUri{ - Uri: "https://storage.blob.core.windows.net/system/Microsoft.Compute/Images/images/packer-osDisk.4085bb15-3644-4641-b9cd-f575918640b4.vhd", - }, - }, - }, - }, - Location: "southcentralus", - }, - }, - } - - testSubject, err := NewArtifact(&template, getFakeSasUrl, "Linux", generatedData()) - if err != nil { - t.Fatalf("err=%s", err) - } - - if testSubject.OSDiskUri != "https://storage.blob.core.windows.net/system/Microsoft.Compute/Images/images/packer-osDisk.4085bb15-3644-4641-b9cd-f575918640b4.vhd" { - t.Errorf("Expected template to be 'https://storage.blob.core.windows.net/system/Microsoft.Compute/Images/images/packer-osDisk.4085bb15-3644-4641-b9cd-f575918640b4.vhd', but got %s", testSubject.OSDiskUri) - } - if testSubject.OSDiskUriReadOnlySas != "SAS-Images/images/packer-osDisk.4085bb15-3644-4641-b9cd-f575918640b4.vhd" { - t.Errorf("Expected template to be 'SAS-Images/images/packer-osDisk.4085bb15-3644-4641-b9cd-f575918640b4.vhd', but got %s", testSubject.OSDiskUriReadOnlySas) - } - if testSubject.TemplateUri != "https://storage.blob.core.windows.net/system/Microsoft.Compute/Images/images/packer-vmTemplate.4085bb15-3644-4641-b9cd-f575918640b4.json" { - t.Errorf("Expected template to be 'https://storage.blob.core.windows.net/system/Microsoft.Compute/Images/images/packer-vmTemplate.4085bb15-3644-4641-b9cd-f575918640b4.json', but got %s", testSubject.TemplateUri) - } - if testSubject.TemplateUriReadOnlySas != "SAS-Images/images/packer-vmTemplate.4085bb15-3644-4641-b9cd-f575918640b4.json" { - t.Errorf("Expected template to be 'SAS-Images/images/packer-vmTemplate.4085bb15-3644-4641-b9cd-f575918640b4.json', but got %s", testSubject.TemplateUriReadOnlySas) - } - if testSubject.StorageAccountLocation != "southcentralus" { - t.Errorf("Expected StorageAccountLocation to be 'southcentral', but got %s", testSubject.StorageAccountLocation) - } - if testSubject.OSType != "Linux" { - t.Errorf("Expected OSType to be 'Linux', but got %s", testSubject.OSType) - } -} - -func TestAdditionalDiskArtifactProperties(t *testing.T) { - template := CaptureTemplate{ - Resources: []CaptureResources{ - { - Properties: CaptureProperties{ - StorageProfile: CaptureStorageProfile{ - OSDisk: CaptureDisk{ - Image: CaptureUri{ - Uri: "https://storage.blob.core.windows.net/system/Microsoft.Compute/Images/images/packer-osDisk.4085bb15-3644-4641-b9cd-f575918640b4.vhd", - }, - }, - DataDisks: []CaptureDisk{ - { - Image: CaptureUri{ - Uri: "https://storage.blob.core.windows.net/system/Microsoft.Compute/Images/images/packer-datadisk-1.4085bb15-3644-4641-b9cd-f575918640b4.vhd", - }, - }, - }, - }, - }, - Location: "southcentralus", - }, - }, - } - - testSubject, err := NewArtifact(&template, getFakeSasUrl, "Linux", generatedData()) - if err != nil { - t.Fatalf("err=%s", err) - } - - if testSubject.OSDiskUri != "https://storage.blob.core.windows.net/system/Microsoft.Compute/Images/images/packer-osDisk.4085bb15-3644-4641-b9cd-f575918640b4.vhd" { - t.Errorf("Expected template to be 'https://storage.blob.core.windows.net/system/Microsoft.Compute/Images/images/packer-osDisk.4085bb15-3644-4641-b9cd-f575918640b4.vhd', but got %s", testSubject.OSDiskUri) - } - if testSubject.OSDiskUriReadOnlySas != "SAS-Images/images/packer-osDisk.4085bb15-3644-4641-b9cd-f575918640b4.vhd" { - t.Errorf("Expected template to be 'SAS-Images/images/packer-osDisk.4085bb15-3644-4641-b9cd-f575918640b4.vhd', but got %s", testSubject.OSDiskUriReadOnlySas) - } - if testSubject.TemplateUri != "https://storage.blob.core.windows.net/system/Microsoft.Compute/Images/images/packer-vmTemplate.4085bb15-3644-4641-b9cd-f575918640b4.json" { - t.Errorf("Expected template to be 'https://storage.blob.core.windows.net/system/Microsoft.Compute/Images/images/packer-vmTemplate.4085bb15-3644-4641-b9cd-f575918640b4.json', but got %s", testSubject.TemplateUri) - } - if testSubject.TemplateUriReadOnlySas != "SAS-Images/images/packer-vmTemplate.4085bb15-3644-4641-b9cd-f575918640b4.json" { - t.Errorf("Expected template to be 'SAS-Images/images/packer-vmTemplate.4085bb15-3644-4641-b9cd-f575918640b4.json', but got %s", testSubject.TemplateUriReadOnlySas) - } - if testSubject.StorageAccountLocation != "southcentralus" { - t.Errorf("Expected StorageAccountLocation to be 'southcentral', but got %s", testSubject.StorageAccountLocation) - } - if testSubject.OSType != "Linux" { - t.Errorf("Expected OSType to be 'Linux', but got %s", testSubject.OSType) - } - if testSubject.AdditionalDisks == nil { - t.Errorf("Expected AdditionalDisks to be not nil") - } - if len(*testSubject.AdditionalDisks) != 1 { - t.Errorf("Expected AdditionalDisks to have one additional disk, but got %d", len(*testSubject.AdditionalDisks)) - } - if (*testSubject.AdditionalDisks)[0].AdditionalDiskUri != "https://storage.blob.core.windows.net/system/Microsoft.Compute/Images/images/packer-datadisk-1.4085bb15-3644-4641-b9cd-f575918640b4.vhd" { - t.Errorf("Expected additional disk uri to be 'https://storage.blob.core.windows.net/system/Microsoft.Compute/Images/images/packer-datadisk-1.4085bb15-3644-4641-b9cd-f575918640b4.vhd', but got %s", (*testSubject.AdditionalDisks)[0].AdditionalDiskUri) - } - if (*testSubject.AdditionalDisks)[0].AdditionalDiskUriReadOnlySas != "SAS-Images/images/packer-datadisk-1.4085bb15-3644-4641-b9cd-f575918640b4.vhd" { - t.Errorf("Expected additional disk sas to be 'SAS-Images/images/packer-datadisk-1.4085bb15-3644-4641-b9cd-f575918640b4.vhd', but got %s", (*testSubject.AdditionalDisks)[0].AdditionalDiskUriReadOnlySas) - } -} - -func TestArtifactOverHyphenatedCaptureUri(t *testing.T) { - template := CaptureTemplate{ - Resources: []CaptureResources{ - { - Properties: CaptureProperties{ - StorageProfile: CaptureStorageProfile{ - OSDisk: CaptureDisk{ - Image: CaptureUri{ - Uri: "https://storage.blob.core.windows.net/system/Microsoft.Compute/Images/images/pac-ker-osDisk.4085bb15-3644-4641-b9cd-f575918640b4.vhd", - }, - }, - }, - }, - Location: "southcentralus", - }, - }, - } - - testSubject, err := NewArtifact(&template, getFakeSasUrl, "Linux", generatedData()) - if err != nil { - t.Fatalf("err=%s", err) - } - - if testSubject.TemplateUri != "https://storage.blob.core.windows.net/system/Microsoft.Compute/Images/images/pac-ker-vmTemplate.4085bb15-3644-4641-b9cd-f575918640b4.json" { - t.Errorf("Expected template to be 'https://storage.blob.core.windows.net/system/Microsoft.Compute/Images/images/pac-ker-vmTemplate.4085bb15-3644-4641-b9cd-f575918640b4.json', but got %s", testSubject.TemplateUri) - } -} - -func TestArtifactRejectMalformedTemplates(t *testing.T) { - template := CaptureTemplate{} - - _, err := NewArtifact(&template, getFakeSasUrl, "Linux", generatedData()) - if err == nil { - t.Fatalf("Expected artifact creation to fail, but it succeeded.") - } -} - -func TestArtifactRejectMalformedStorageUri(t *testing.T) { - template := CaptureTemplate{ - Resources: []CaptureResources{ - { - Properties: CaptureProperties{ - StorageProfile: CaptureStorageProfile{ - OSDisk: CaptureDisk{ - Image: CaptureUri{ - Uri: "bark", - }, - }, - }, - }, - }, - }, - } - - _, err := NewArtifact(&template, getFakeSasUrl, "Linux", generatedData()) - if err == nil { - t.Fatalf("Expected artifact creation to fail, but it succeeded.") - } -} - -func TestArtifactState_StateData(t *testing.T) { - expectedData := "this is the data" - artifact := &Artifact{ - StateData: map[string]interface{}{"state_data": expectedData}, - } - - // Valid state - result := artifact.State("state_data") - if result != expectedData { - t.Fatalf("Bad: State data was %s instead of %s", result, expectedData) - } - - // Invalid state - result = artifact.State("invalid_key") - if result != nil { - t.Fatalf("Bad: State should be nil for invalid state data name") - } - - // Nil StateData should not fail and should return nil - artifact = &Artifact{} - result = artifact.State("key") - if result != nil { - t.Fatalf("Bad: State should be nil for nil StateData") - } -} diff --git a/builder/azure/arm/azure_client.go b/builder/azure/arm/azure_client.go deleted file mode 100644 index 26dadc336..000000000 --- a/builder/azure/arm/azure_client.go +++ /dev/null @@ -1,311 +0,0 @@ -package arm - -import ( - "context" - "encoding/json" - "fmt" - "math" - "net/http" - "net/url" - "os" - "strconv" - "time" - - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2018-04-01/compute" - newCompute "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-03-01/compute" - "github.com/Azure/azure-sdk-for-go/services/keyvault/mgmt/2018-02-14/keyvault" - "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2018-01-01/network" - "github.com/Azure/azure-sdk-for-go/services/resources/mgmt/2018-02-01/resources" - 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" - "github.com/Azure/go-autorest/autorest/adal" - "github.com/Azure/go-autorest/autorest/azure" - "github.com/hashicorp/packer-plugin-sdk/useragent" - "github.com/hashicorp/packer/builder/azure/common" - "github.com/hashicorp/packer/builder/azure/version" -) - -const ( - EnvPackerLogAzureMaxLen = "PACKER_LOG_AZURE_MAXLEN" -) - -type AzureClient struct { - storage.BlobStorageClient - resources.DeploymentsClient - resources.DeploymentOperationsClient - resources.GroupsClient - network.PublicIPAddressesClient - network.InterfacesClient - network.SubnetsClient - network.VirtualNetworksClient - network.SecurityGroupsClient - compute.ImagesClient - compute.VirtualMachinesClient - common.VaultClient - armStorage.AccountsClient - compute.DisksClient - compute.SnapshotsClient - newCompute.GalleryImageVersionsClient - newCompute.GalleryImagesClient - - InspectorMaxLength int - Template *CaptureTemplate - LastError azureErrorResponse - VaultClientDelete keyvault.VaultsClient -} - -func getCaptureResponse(body string) *CaptureTemplate { - var operation CaptureOperation - err := json.Unmarshal([]byte(body), &operation) - if err != nil { - return nil - } - - if operation.Properties != nil && operation.Properties.Output != nil { - return operation.Properties.Output - } - - return nil -} - -// HACK(chrboum): This method is a hack. It was written to work around this issue -// (https://github.com/Azure/azure-sdk-for-go/issues/307) and to an extent this -// issue (https://github.com/Azure/azure-rest-api-specs/issues/188). -// -// Capturing a VM is a long running operation that requires polling. There are -// couple different forms of polling, and the end result of a poll operation is -// discarded by the SDK. It is expected that any discarded data can be re-fetched, -// so discarding it has minimal impact. Unfortunately, there is no way to re-fetch -// the template returned by a capture call that I am aware of. -// -// If the second issue were fixed the VM ID would be included when GET'ing a VM. The -// VM ID could be used to locate the captured VHD, and captured template. -// Unfortunately, the VM ID is not included so this method cannot be used either. -// -// This code captures the template and saves it to the client (the AzureClient type). -// It expects that the capture API is called only once, or rather you only care that the -// last call's value is important because subsequent requests are not persisted. There -// is no care given to multiple threads writing this value because for our use case -// it does not matter. -func templateCapture(client *AzureClient) autorest.RespondDecorator { - return func(r autorest.Responder) autorest.Responder { - return autorest.ResponderFunc(func(resp *http.Response) error { - body, bodyString := handleBody(resp.Body, math.MaxInt64) - resp.Body = body - - captureTemplate := getCaptureResponse(bodyString) - if captureTemplate != nil { - client.Template = captureTemplate - } - - return r.Respond(resp) - }) - } -} - -func errorCapture(client *AzureClient) autorest.RespondDecorator { - return func(r autorest.Responder) autorest.Responder { - return autorest.ResponderFunc(func(resp *http.Response) error { - body, bodyString := handleBody(resp.Body, math.MaxInt64) - resp.Body = body - - errorResponse := newAzureErrorResponse(bodyString) - if errorResponse != nil { - client.LastError = *errorResponse - } - - return r.Respond(resp) - }) - } -} - -// WAITING(chrboum): I have logged https://github.com/Azure/azure-sdk-for-go/issues/311 to get this -// method included in the SDK. It has been accepted, and I'll cut over to the official way -// once it ships. -func byConcatDecorators(decorators ...autorest.RespondDecorator) autorest.RespondDecorator { - return func(r autorest.Responder) autorest.Responder { - return autorest.DecorateResponder(r, decorators...) - } -} - -func NewAzureClient(subscriptionID, sigSubscriptionID, resourceGroupName, storageAccountName string, - cloud *azure.Environment, sharedGalleryTimeout time.Duration, pollingDuration time.Duration, - servicePrincipalToken, servicePrincipalTokenVault *adal.ServicePrincipalToken) (*AzureClient, error) { - - var azureClient = &AzureClient{} - - maxlen := getInspectorMaxLength() - - azureClient.DeploymentsClient = resources.NewDeploymentsClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID) - azureClient.DeploymentsClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken) - azureClient.DeploymentsClient.RequestInspector = withInspection(maxlen) - azureClient.DeploymentsClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient)) - azureClient.DeploymentsClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(version.AzurePluginVersion.FormattedVersion()), azureClient.DeploymentsClient.UserAgent) - azureClient.DeploymentsClient.Client.PollingDuration = pollingDuration - - azureClient.DeploymentOperationsClient = resources.NewDeploymentOperationsClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID) - azureClient.DeploymentOperationsClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken) - azureClient.DeploymentOperationsClient.RequestInspector = withInspection(maxlen) - azureClient.DeploymentOperationsClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient)) - azureClient.DeploymentOperationsClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(version.AzurePluginVersion.FormattedVersion()), azureClient.DeploymentOperationsClient.UserAgent) - azureClient.DeploymentOperationsClient.Client.PollingDuration = pollingDuration - - azureClient.DisksClient = compute.NewDisksClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID) - azureClient.DisksClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken) - azureClient.DisksClient.RequestInspector = withInspection(maxlen) - azureClient.DisksClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient)) - azureClient.DisksClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(version.AzurePluginVersion.FormattedVersion()), azureClient.DisksClient.UserAgent) - azureClient.DisksClient.Client.PollingDuration = pollingDuration - - azureClient.GroupsClient = resources.NewGroupsClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID) - azureClient.GroupsClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken) - azureClient.GroupsClient.RequestInspector = withInspection(maxlen) - azureClient.GroupsClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient)) - azureClient.GroupsClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(version.AzurePluginVersion.FormattedVersion()), azureClient.GroupsClient.UserAgent) - azureClient.GroupsClient.Client.PollingDuration = pollingDuration - - azureClient.ImagesClient = compute.NewImagesClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID) - azureClient.ImagesClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken) - azureClient.ImagesClient.RequestInspector = withInspection(maxlen) - azureClient.ImagesClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient)) - azureClient.ImagesClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(version.AzurePluginVersion.FormattedVersion()), azureClient.ImagesClient.UserAgent) - azureClient.ImagesClient.Client.PollingDuration = pollingDuration - - azureClient.InterfacesClient = network.NewInterfacesClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID) - azureClient.InterfacesClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken) - azureClient.InterfacesClient.RequestInspector = withInspection(maxlen) - azureClient.InterfacesClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient)) - azureClient.InterfacesClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(version.AzurePluginVersion.FormattedVersion()), azureClient.InterfacesClient.UserAgent) - azureClient.InterfacesClient.Client.PollingDuration = pollingDuration - - azureClient.SubnetsClient = network.NewSubnetsClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID) - azureClient.SubnetsClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken) - azureClient.SubnetsClient.RequestInspector = withInspection(maxlen) - azureClient.SubnetsClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient)) - azureClient.SubnetsClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(version.AzurePluginVersion.FormattedVersion()), azureClient.SubnetsClient.UserAgent) - azureClient.SubnetsClient.Client.PollingDuration = pollingDuration - - azureClient.VirtualNetworksClient = network.NewVirtualNetworksClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID) - azureClient.VirtualNetworksClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken) - azureClient.VirtualNetworksClient.RequestInspector = withInspection(maxlen) - azureClient.VirtualNetworksClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient)) - azureClient.VirtualNetworksClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(version.AzurePluginVersion.FormattedVersion()), azureClient.VirtualNetworksClient.UserAgent) - azureClient.VirtualNetworksClient.Client.PollingDuration = pollingDuration - - azureClient.SecurityGroupsClient = network.NewSecurityGroupsClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID) - azureClient.SecurityGroupsClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken) - azureClient.SecurityGroupsClient.RequestInspector = withInspection(maxlen) - azureClient.SecurityGroupsClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient)) - azureClient.SecurityGroupsClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(version.AzurePluginVersion.FormattedVersion()), azureClient.SecurityGroupsClient.UserAgent) - - azureClient.PublicIPAddressesClient = network.NewPublicIPAddressesClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID) - azureClient.PublicIPAddressesClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken) - azureClient.PublicIPAddressesClient.RequestInspector = withInspection(maxlen) - azureClient.PublicIPAddressesClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient)) - azureClient.PublicIPAddressesClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(version.AzurePluginVersion.FormattedVersion()), azureClient.PublicIPAddressesClient.UserAgent) - azureClient.PublicIPAddressesClient.Client.PollingDuration = pollingDuration - - azureClient.VirtualMachinesClient = compute.NewVirtualMachinesClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID) - azureClient.VirtualMachinesClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken) - azureClient.VirtualMachinesClient.RequestInspector = withInspection(maxlen) - azureClient.VirtualMachinesClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), templateCapture(azureClient), errorCapture(azureClient)) - azureClient.VirtualMachinesClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(version.AzurePluginVersion.FormattedVersion()), azureClient.VirtualMachinesClient.UserAgent) - azureClient.VirtualMachinesClient.Client.PollingDuration = pollingDuration - - azureClient.SnapshotsClient = compute.NewSnapshotsClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID) - azureClient.SnapshotsClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken) - azureClient.SnapshotsClient.RequestInspector = withInspection(maxlen) - azureClient.SnapshotsClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient)) - azureClient.SnapshotsClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(version.AzurePluginVersion.FormattedVersion()), azureClient.SnapshotsClient.UserAgent) - azureClient.SnapshotsClient.Client.PollingDuration = pollingDuration - - azureClient.AccountsClient = armStorage.NewAccountsClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID) - azureClient.AccountsClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken) - azureClient.AccountsClient.RequestInspector = withInspection(maxlen) - azureClient.AccountsClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient)) - azureClient.AccountsClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(version.AzurePluginVersion.FormattedVersion()), azureClient.AccountsClient.UserAgent) - azureClient.AccountsClient.Client.PollingDuration = pollingDuration - - azureClient.GalleryImageVersionsClient = newCompute.NewGalleryImageVersionsClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID) - azureClient.GalleryImageVersionsClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken) - azureClient.GalleryImageVersionsClient.RequestInspector = withInspection(maxlen) - azureClient.GalleryImageVersionsClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient)) - azureClient.GalleryImageVersionsClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(version.AzurePluginVersion.FormattedVersion()), azureClient.GalleryImageVersionsClient.UserAgent) - azureClient.GalleryImageVersionsClient.Client.PollingDuration = sharedGalleryTimeout - if sigSubscriptionID != "" { - azureClient.GalleryImageVersionsClient.SubscriptionID = sigSubscriptionID - } - - azureClient.GalleryImagesClient = newCompute.NewGalleryImagesClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID) - azureClient.GalleryImagesClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken) - azureClient.GalleryImagesClient.RequestInspector = withInspection(maxlen) - azureClient.GalleryImagesClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient)) - azureClient.GalleryImagesClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(version.AzurePluginVersion.FormattedVersion()), azureClient.GalleryImagesClient.UserAgent) - azureClient.GalleryImagesClient.Client.PollingDuration = pollingDuration - if sigSubscriptionID != "" { - azureClient.GalleryImagesClient.SubscriptionID = sigSubscriptionID - } - - keyVaultURL, err := url.Parse(cloud.KeyVaultEndpoint) - if err != nil { - return nil, err - } - - azureClient.VaultClient = common.NewVaultClient(*keyVaultURL) - azureClient.VaultClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalTokenVault) - azureClient.VaultClient.RequestInspector = withInspection(maxlen) - azureClient.VaultClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient)) - azureClient.VaultClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(version.AzurePluginVersion.FormattedVersion()), azureClient.VaultClient.UserAgent) - azureClient.VaultClient.Client.PollingDuration = pollingDuration - - // This client is different than the above because it manages the vault - // itself rather than the contents of the vault. - azureClient.VaultClientDelete = keyvault.NewVaultsClient(subscriptionID) - azureClient.VaultClientDelete.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken) - azureClient.VaultClientDelete.RequestInspector = withInspection(maxlen) - azureClient.VaultClientDelete.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient)) - azureClient.VaultClientDelete.UserAgent = fmt.Sprintf("%s %s", useragent.String(version.AzurePluginVersion.FormattedVersion()), azureClient.VaultClientDelete.UserAgent) - azureClient.VaultClientDelete.Client.PollingDuration = pollingDuration - - // If this is a managed disk build, this should be ignored. - if resourceGroupName != "" && storageAccountName != "" { - accountKeys, err := azureClient.AccountsClient.ListKeys(context.TODO(), resourceGroupName, storageAccountName) - if err != nil { - return nil, err - } - - storageClient, err := storage.NewClient( - storageAccountName, - *(*accountKeys.Keys)[0].Value, - cloud.StorageEndpointSuffix, - storage.DefaultAPIVersion, - true /*useHttps*/) - - if err != nil { - return nil, err - } - - azureClient.BlobStorageClient = storageClient.GetBlobService() - } - - return azureClient, nil -} - -func getInspectorMaxLength() int64 { - value, ok := os.LookupEnv(EnvPackerLogAzureMaxLen) - if !ok { - return math.MaxInt64 - } - - i, err := strconv.ParseInt(value, 10, 64) - if err != nil { - return 0 - } - - if i < 0 { - return 0 - } - - return i -} diff --git a/builder/azure/arm/azure_error_response.go b/builder/azure/arm/azure_error_response.go deleted file mode 100644 index e8e50c9eb..000000000 --- a/builder/azure/arm/azure_error_response.go +++ /dev/null @@ -1,67 +0,0 @@ -package arm - -import ( - "bytes" - "encoding/json" - "fmt" -) - -type azureErrorDetails struct { - Code string `json:"code"` - Message string `json:"message"` - Details []azureErrorDetails `json:"details"` -} - -type azureErrorResponse struct { - ErrorDetails azureErrorDetails `json:"error"` -} - -func newAzureErrorResponse(s string) *azureErrorResponse { - var errorResponse azureErrorResponse - err := json.Unmarshal([]byte(s), &errorResponse) - if err == nil { - return &errorResponse - } - - return nil -} - -func (e *azureErrorDetails) isEmpty() bool { - return e.Code == "" -} - -func (e *azureErrorResponse) isEmpty() bool { - return e.ErrorDetails.isEmpty() -} - -func (e *azureErrorResponse) Error() string { - var buf bytes.Buffer - //buf.WriteString("-=-=- ERROR -=-=-") - formatAzureErrorResponse(e.ErrorDetails, &buf, "") - //buf.WriteString("-=-=- ERROR -=-=-") - return buf.String() -} - -// format a Azure Error Response by recursing through the JSON structure. -// -// Errors may contain nested errors, which are JSON documents that have been -// serialized and escaped. Keep following this nesting all the way down... -func formatAzureErrorResponse(error azureErrorDetails, buf *bytes.Buffer, indent string) { - if error.isEmpty() { - return - } - - buf.WriteString(fmt.Sprintf("ERROR: %s-> %s : %s\n", indent, error.Code, error.Message)) - for _, x := range error.Details { - newIndent := fmt.Sprintf("%s ", indent) - - var aer azureErrorResponse - err := json.Unmarshal([]byte(x.Message), &aer) - if err == nil { - buf.WriteString(fmt.Sprintf("ERROR: %s-> %s\n", newIndent, x.Code)) - formatAzureErrorResponse(aer.ErrorDetails, buf, newIndent) - } else { - buf.WriteString(fmt.Sprintf("ERROR: %s-> %s : %s\n", newIndent, x.Code, x.Message)) - } - } -} diff --git a/builder/azure/arm/azure_error_response_test.TestAzureErrorNestedShouldFormat.approved.txt b/builder/azure/arm/azure_error_response_test.TestAzureErrorNestedShouldFormat.approved.txt deleted file mode 100644 index 39d4deeec..000000000 --- a/builder/azure/arm/azure_error_response_test.TestAzureErrorNestedShouldFormat.approved.txt +++ /dev/null @@ -1,4 +0,0 @@ -ERROR: -> DeploymentFailed : At least one resource deployment operation failed. Please list deployment operations for details. Please see https://aka.ms/arm-debug for usage details. -ERROR: -> BadRequest -ERROR: -> InvalidRequestFormat : Cannot parse the request. -ERROR: -> InvalidJson : Error converting value "playground" to type 'Microsoft.WindowsAzure.Networking.Nrp.Frontend.Contract.Csm.Public.IpAllocationMethod'. Path 'properties.publicIPAllocationMethod', line 1, position 130. diff --git a/builder/azure/arm/azure_error_response_test.TestAzureErrorSimpleShouldFormat.approved.txt b/builder/azure/arm/azure_error_response_test.TestAzureErrorSimpleShouldFormat.approved.txt deleted file mode 100644 index 4b4834c62..000000000 --- a/builder/azure/arm/azure_error_response_test.TestAzureErrorSimpleShouldFormat.approved.txt +++ /dev/null @@ -1 +0,0 @@ -ERROR: -> ResourceNotFound : The Resource 'Microsoft.Compute/images/PackerUbuntuImage' under resource group 'packer-test00' was not found. diff --git a/builder/azure/arm/azure_error_response_test.go b/builder/azure/arm/azure_error_response_test.go deleted file mode 100644 index 91f03ed8d..000000000 --- a/builder/azure/arm/azure_error_response_test.go +++ /dev/null @@ -1,77 +0,0 @@ -package arm - -import ( - "strings" - "testing" - - approvaltests "github.com/approvals/go-approval-tests" - "github.com/hashicorp/packer-plugin-sdk/json" -) - -const AzureErrorSimple = `{"error":{"code":"ResourceNotFound","message":"The Resource 'Microsoft.Compute/images/PackerUbuntuImage' under resource group 'packer-test00' was not found."}}` -const AzureErrorNested = `{"status":"Failed","error":{"code":"DeploymentFailed","message":"At least one resource deployment operation failed. Please list deployment operations for details. Please see https://aka.ms/arm-debug for usage details.","details":[{"code":"BadRequest","message":"{\r\n \"error\": {\r\n \"code\": \"InvalidRequestFormat\",\r\n \"message\": \"Cannot parse the request.\",\r\n \"details\": [\r\n {\r\n \"code\": \"InvalidJson\",\r\n \"message\": \"Error converting value \\\"playground\\\" to type 'Microsoft.WindowsAzure.Networking.Nrp.Frontend.Contract.Csm.Public.IpAllocationMethod'. Path 'properties.publicIPAllocationMethod', line 1, position 130.\"\r\n }\r\n ]\r\n }\r\n}"}]}}` - -func TestAzureErrorSimpleShouldUnmarshal(t *testing.T) { - var azureErrorResponse azureErrorResponse - err := json.Unmarshal([]byte(AzureErrorSimple), &azureErrorResponse) - if err != nil { - t.Fatal(err) - } - - if azureErrorResponse.ErrorDetails.Code != "ResourceNotFound" { - t.Errorf("Error.Code") - } - if azureErrorResponse.ErrorDetails.Message != "The Resource 'Microsoft.Compute/images/PackerUbuntuImage' under resource group 'packer-test00' was not found." { - t.Errorf("Error.Message") - } -} - -func TestAzureErrorNestedShouldUnmarshal(t *testing.T) { - var azureError azureErrorResponse - err := json.Unmarshal([]byte(AzureErrorNested), &azureError) - if err != nil { - t.Fatal(err) - } - - if azureError.ErrorDetails.Code != "DeploymentFailed" { - t.Errorf("Error.Code") - } - if !strings.HasPrefix(azureError.ErrorDetails.Message, "At least one resource deployment operation failed") { - t.Errorf("Error.Message") - } -} - -func TestAzureErrorEmptyShouldFormat(t *testing.T) { - var aer azureErrorResponse - s := aer.Error() - - if s != "" { - t.Fatalf("Expected \"\", but got %s", aer.Error()) - } -} - -func TestAzureErrorSimpleShouldFormat(t *testing.T) { - var azureErrorResponse azureErrorResponse - err := json.Unmarshal([]byte(AzureErrorSimple), &azureErrorResponse) - if err != nil { - t.Fatal(err) - } - - err = approvaltests.VerifyString(t, azureErrorResponse.Error()) - if err != nil { - t.Fatal(err) - } -} - -func TestAzureErrorNestedShouldFormat(t *testing.T) { - var azureErrorResponse azureErrorResponse - err := json.Unmarshal([]byte(AzureErrorNested), &azureErrorResponse) - if err != nil { - t.Fatal(err) - } - - err = approvaltests.VerifyString(t, azureErrorResponse.Error()) - if err != nil { - t.Fatal(err) - } -} diff --git a/builder/azure/arm/builder.go b/builder/azure/arm/builder.go deleted file mode 100644 index a18434914..000000000 --- a/builder/azure/arm/builder.go +++ /dev/null @@ -1,496 +0,0 @@ -package arm - -import ( - "context" - "errors" - "fmt" - "log" - "os" - "runtime" - "strings" - "time" - - 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" - "github.com/dgrijalva/jwt-go" - "github.com/hashicorp/hcl/v2/hcldec" - "github.com/hashicorp/packer-plugin-sdk/communicator" - "github.com/hashicorp/packer-plugin-sdk/multistep" - "github.com/hashicorp/packer-plugin-sdk/multistep/commonsteps" - packersdk "github.com/hashicorp/packer-plugin-sdk/packer" - packerAzureCommon "github.com/hashicorp/packer/builder/azure/common" - "github.com/hashicorp/packer/builder/azure/common/constants" - "github.com/hashicorp/packer/builder/azure/common/lin" -) - -type Builder struct { - config Config - stateBag multistep.StateBag - runner multistep.Runner -} - -const ( - DefaultSasBlobContainer = "system/Microsoft.Compute" - DefaultSecretName = "packerKeyVaultSecret" -) - -func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() } - -func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) { - warnings, errs := b.config.Prepare(raws...) - if errs != nil { - return nil, warnings, errs - } - - b.stateBag = new(multistep.BasicStateBag) - b.configureStateBag(b.stateBag) - b.setTemplateParameters(b.stateBag) - b.setImageParameters(b.stateBag) - - return nil, warnings, errs -} - -func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) (packersdk.Artifact, error) { - - ui.Say("Running builder ...") - - ctx, cancel := context.WithCancel(ctx) - defer cancel() - - // FillParameters function captures authType and sets defaults. - err := b.config.ClientConfig.FillParameters() - if err != nil { - return nil, err - } - - //When running Packer on an Azure instance using Managed Identity, FillParameters will update SubscriptionID from the instance - // so lets make sure to update our state bag with the valid subscriptionID. - if b.config.isManagedImage() && b.config.SharedGalleryDestination.SigDestinationGalleryName != "" { - b.stateBag.Put(constants.ArmManagedImageSubscription, b.config.ClientConfig.SubscriptionID) - } - - log.Print(":: Configuration") - packerAzureCommon.DumpConfig(&b.config, func(s string) { log.Print(s) }) - - b.stateBag.Put("hook", hook) - b.stateBag.Put(constants.Ui, ui) - - spnCloud, spnKeyVault, err := b.getServicePrincipalTokens(ui.Say) - if err != nil { - return nil, err - } - - ui.Message("Creating Azure Resource Manager (ARM) client ...") - azureClient, err := NewAzureClient( - b.config.ClientConfig.SubscriptionID, - b.config.SharedGalleryDestination.SigDestinationSubscription, - b.config.ResourceGroupName, - b.config.StorageAccount, - b.config.ClientConfig.CloudEnvironment(), - b.config.SharedGalleryTimeout, - b.config.PollingDurationTimeout, - spnCloud, - spnKeyVault) - - if err != nil { - return nil, err - } - - resolver := newResourceResolver(azureClient) - if err := resolver.Resolve(&b.config); err != nil { - return nil, err - } - if b.config.ClientConfig.ObjectID == "" { - b.config.ClientConfig.ObjectID = getObjectIdFromToken(ui, spnCloud) - } else { - ui.Message("You have provided Object_ID which is no longer needed, azure packer builder determines this dynamically from the authentication token") - } - - if b.config.ClientConfig.ObjectID == "" && b.config.OSType != constants.Target_Linux { - return nil, fmt.Errorf("could not determine the ObjectID for the user, which is required for Windows builds") - } - - if b.config.isManagedImage() { - _, err := azureClient.GroupsClient.Get(ctx, b.config.ManagedImageResourceGroupName) - if err != nil { - return nil, fmt.Errorf("Cannot locate the managed image resource group %s.", b.config.ManagedImageResourceGroupName) - } - - // If a managed image already exists it cannot be overwritten. - _, err = azureClient.ImagesClient.Get(ctx, b.config.ManagedImageResourceGroupName, b.config.ManagedImageName, "") - if err == nil { - 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)) - f, err := azureClient.ImagesClient.Delete(ctx, b.config.ManagedImageResourceGroupName, b.config.ManagedImageName) - if err == nil { - err = f.WaitForCompletionRef(ctx, azureClient.ImagesClient.Client) - } - 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) - } - } - } 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 https://www.packer.io/docs/builders/azure/arm#azure-arm-builder-specific-options") - } - - if b.config.BuildResourceGroupName != "" { - group, err := azureClient.GroupsClient.Get(ctx, b.config.BuildResourceGroupName) - if err != nil { - return nil, fmt.Errorf("Cannot locate the existing build resource resource group %s.", b.config.BuildResourceGroupName) - } - - b.config.Location = *group.Location - } - - b.config.validateLocationZoneResiliency(ui.Say) - - if b.config.StorageAccount != "" { - account, err := b.getBlobAccount(ctx, azureClient, b.config.ResourceGroupName, b.config.StorageAccount) - if err != nil { - return nil, err - } - b.config.storageAccountBlobEndpoint = *account.AccountProperties.PrimaryEndpoints.Blob - - 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) - } - } - - endpointConnectType := PublicEndpoint - if b.isPublicPrivateNetworkCommunication() && b.isPrivateNetworkCommunication() { - endpointConnectType = PublicEndpointInPrivateNetwork - } else if b.isPrivateNetworkCommunication() { - endpointConnectType = PrivateEndpoint - } - - b.setRuntimeParameters(b.stateBag) - b.setTemplateParameters(b.stateBag) - b.setImageParameters(b.stateBag) - - deploymentName := b.stateBag.Get(constants.ArmDeploymentName).(string) - - // For Managed Images, validate that Shared Gallery Image exists before publishing to SIG - if b.config.isManagedImage() && b.config.SharedGalleryDestination.SigDestinationGalleryName != "" { - _, err = azureClient.GalleryImagesClient.Get(ctx, b.config.SharedGalleryDestination.SigDestinationResourceGroup, b.config.SharedGalleryDestination.SigDestinationGalleryName, b.config.SharedGalleryDestination.SigDestinationImageName) - if err != nil { - return nil, fmt.Errorf("the Shared Gallery Image to which to publish the managed image version to does not exist in the resource group %s", b.config.SharedGalleryDestination.SigDestinationResourceGroup) - } - // SIG requires that replication regions include the region in which the Managed Image resides - managedImageLocation := normalizeAzureRegion(b.stateBag.Get(constants.ArmLocation).(string)) - foundMandatoryReplicationRegion := false - var normalizedReplicationRegions []string - for _, region := range b.config.SharedGalleryDestination.SigDestinationReplicationRegions { - // change region to lower-case and strip spaces - normalizedRegion := normalizeAzureRegion(region) - normalizedReplicationRegions = append(normalizedReplicationRegions, normalizedRegion) - if strings.EqualFold(normalizedRegion, managedImageLocation) { - foundMandatoryReplicationRegion = true - continue - } - } - if foundMandatoryReplicationRegion == false { - b.config.SharedGalleryDestination.SigDestinationReplicationRegions = append(normalizedReplicationRegions, managedImageLocation) - } - b.stateBag.Put(constants.ArmManagedImageSharedGalleryReplicationRegions, b.config.SharedGalleryDestination.SigDestinationReplicationRegions) - } - - var steps []multistep.Step - if b.config.OSType == constants.Target_Linux { - steps = []multistep.Step{ - NewStepCreateResourceGroup(azureClient, ui), - NewStepValidateTemplate(azureClient, ui, &b.config, GetVirtualMachineDeployment), - NewStepDeployTemplate(azureClient, ui, &b.config, deploymentName, GetVirtualMachineDeployment), - NewStepGetIPAddress(azureClient, ui, endpointConnectType), - &communicator.StepConnectSSH{ - Config: &b.config.Comm, - Host: lin.SSHHost, - SSHConfig: b.config.Comm.SSHConfigFunc(), - }, - &commonsteps.StepProvision{}, - &commonsteps.StepCleanupTempKeys{ - Comm: &b.config.Comm, - }, - NewStepGetOSDisk(azureClient, ui), - NewStepGetAdditionalDisks(azureClient, ui), - NewStepPowerOffCompute(azureClient, ui), - NewStepSnapshotOSDisk(azureClient, ui, &b.config), - NewStepSnapshotDataDisks(azureClient, ui, &b.config), - NewStepCaptureImage(azureClient, ui), - NewStepPublishToSharedImageGallery(azureClient, ui, &b.config), - } - } else if b.config.OSType == constants.Target_Windows { - steps = []multistep.Step{ - NewStepCreateResourceGroup(azureClient, ui), - } - if b.config.BuildKeyVaultName == "" { - keyVaultDeploymentName := b.stateBag.Get(constants.ArmKeyVaultDeploymentName).(string) - steps = append(steps, - NewStepValidateTemplate(azureClient, ui, &b.config, GetKeyVaultDeployment), - NewStepDeployTemplate(azureClient, ui, &b.config, keyVaultDeploymentName, GetKeyVaultDeployment), - ) - } else { - steps = append(steps, NewStepCertificateInKeyVault(&azureClient.VaultClient, ui, &b.config)) - } - steps = append(steps, - NewStepGetCertificate(azureClient, ui), - NewStepSetCertificate(&b.config, ui), - NewStepValidateTemplate(azureClient, ui, &b.config, GetVirtualMachineDeployment), - NewStepDeployTemplate(azureClient, ui, &b.config, deploymentName, GetVirtualMachineDeployment), - NewStepGetIPAddress(azureClient, ui, endpointConnectType), - &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.Password, - }, nil - }, - }, - &commonsteps.StepProvision{}, - NewStepGetOSDisk(azureClient, ui), - NewStepGetAdditionalDisks(azureClient, ui), - NewStepPowerOffCompute(azureClient, ui), - NewStepSnapshotOSDisk(azureClient, ui, &b.config), - NewStepSnapshotDataDisks(azureClient, ui, &b.config), - NewStepCaptureImage(azureClient, ui), - NewStepPublishToSharedImageGallery(azureClient, ui, &b.config), - ) - } else { - return nil, fmt.Errorf("Builder does not support the os_type '%s'", b.config.OSType) - } - - 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)) - - if len(b.config.Comm.SSHPrivateKey) != 0 { - 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) - } - } - - b.runner = commonsteps.NewRunner(steps, b.config.PackerConfig, ui) - b.runner.Run(ctx, 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.") - } - - getSasUrlFunc := func(name string) string { - blob := azureClient.BlobStorageClient.GetContainerReference(DefaultSasBlobContainer).GetBlobReference(name) - options := storage.BlobSASOptions{} - options.BlobServiceSASPermissions.Read = true - options.Expiry = time.Now().AddDate(0, 1, 0).UTC() // one month - sasUrl, _ := blob.GetSASURI(options) - return sasUrl - } - - generatedData := map[string]interface{}{"generated_data": b.stateBag.Get("generated_data")} - if b.config.isManagedImage() { - managedImageID := fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Compute/images/%s", - b.config.ClientConfig.SubscriptionID, b.config.ManagedImageResourceGroupName, b.config.ManagedImageName) - if b.config.SharedGalleryDestination.SigDestinationGalleryName != "" { - return NewManagedImageArtifactWithSIGAsDestination(b.config.OSType, - b.config.ManagedImageResourceGroupName, - b.config.ManagedImageName, - b.config.Location, - managedImageID, - b.config.ManagedImageOSDiskSnapshotName, - b.config.ManagedImageDataDiskSnapshotPrefix, - b.stateBag.Get(constants.ArmManagedImageSharedGalleryId).(string), - generatedData) - } else if template, ok := b.stateBag.GetOk(constants.ArmCaptureTemplate); ok { - return NewManagedImageArtifact(b.config.OSType, - b.config.ManagedImageResourceGroupName, - b.config.ManagedImageName, - b.config.Location, - managedImageID, - b.config.ManagedImageOSDiskSnapshotName, - b.config.ManagedImageDataDiskSnapshotPrefix, - generatedData, - b.stateBag.Get(constants.ArmKeepOSDisk).(bool), - template.(*CaptureTemplate), - getSasUrlFunc) - } - return NewManagedImageArtifact(b.config.OSType, - b.config.ManagedImageResourceGroupName, - b.config.ManagedImageName, - b.config.Location, - managedImageID, - b.config.ManagedImageOSDiskSnapshotName, - b.config.ManagedImageDataDiskSnapshotPrefix, - generatedData, - b.stateBag.Get(constants.ArmKeepOSDisk).(bool), - nil, - getSasUrlFunc) - } else if template, ok := b.stateBag.GetOk(constants.ArmCaptureTemplate); ok { - return NewArtifact( - template.(*CaptureTemplate), - getSasUrlFunc, - b.config.OSType, - generatedData) - } - - return &Artifact{ - StateData: generatedData, - }, nil -} - -func (b *Builder) writeSSHPrivateKey(ui packersdk.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(b.config.Comm.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)) - } - } -} - -func (b *Builder) isPublicPrivateNetworkCommunication() bool { - return DefaultPrivateVirtualNetworkWithPublicIp != b.config.PrivateVirtualNetworkWithPublicIp -} - -func (b *Builder) isPrivateNetworkCommunication() bool { - return b.config.VirtualNetworkName != "" -} - -func equalLocation(location1, location2 string) bool { - return strings.EqualFold(canonicalizeLocation(location1), canonicalizeLocation(location2)) -} - -func canonicalizeLocation(location string) string { - return strings.Replace(location, " ", "", -1) -} - -func (b *Builder) getBlobAccount(ctx context.Context, client *AzureClient, resourceGroupName string, storageAccountName string) (*armstorage.Account, error) { - account, err := client.AccountsClient.GetProperties(ctx, resourceGroupName, storageAccountName) - if err != nil { - return nil, err - } - - return &account, err -} - -func (b *Builder) configureStateBag(stateBag multistep.StateBag) { - stateBag.Put(constants.AuthorizedKey, b.config.sshAuthorizedKey) - - stateBag.Put(constants.ArmTags, packerAzureCommon.MapToAzureTags(b.config.AzureTags)) - stateBag.Put(constants.ArmComputeName, b.config.tmpComputeName) - stateBag.Put(constants.ArmDeploymentName, b.config.tmpDeploymentName) - - if b.config.OSType == constants.Target_Windows && b.config.BuildKeyVaultName == "" { - stateBag.Put(constants.ArmKeyVaultDeploymentName, fmt.Sprintf("kv%s", b.config.tmpDeploymentName)) - } - - stateBag.Put(constants.ArmKeyVaultName, b.config.tmpKeyVaultName) - stateBag.Put(constants.ArmIsExistingKeyVault, false) - if b.config.BuildKeyVaultName != "" { - stateBag.Put(constants.ArmKeyVaultName, b.config.BuildKeyVaultName) - b.config.tmpKeyVaultName = b.config.BuildKeyVaultName - stateBag.Put(constants.ArmIsExistingKeyVault, true) - } - - stateBag.Put(constants.ArmNicName, b.config.tmpNicName) - stateBag.Put(constants.ArmPublicIPAddressName, b.config.tmpPublicIPAddressName) - stateBag.Put(constants.ArmResourceGroupName, b.config.BuildResourceGroupName) - stateBag.Put(constants.ArmIsExistingResourceGroup, true) - - if b.config.tmpResourceGroupName != "" { - stateBag.Put(constants.ArmResourceGroupName, b.config.tmpResourceGroupName) - stateBag.Put(constants.ArmIsExistingResourceGroup, false) - - if b.config.BuildResourceGroupName != "" { - stateBag.Put(constants.ArmDoubleResourceGroupNameSet, true) - } - } - - stateBag.Put(constants.ArmStorageAccountName, b.config.StorageAccount) - stateBag.Put(constants.ArmIsManagedImage, b.config.isManagedImage()) - stateBag.Put(constants.ArmManagedImageResourceGroupName, b.config.ManagedImageResourceGroupName) - stateBag.Put(constants.ArmManagedImageName, b.config.ManagedImageName) - stateBag.Put(constants.ArmManagedImageOSDiskSnapshotName, b.config.ManagedImageOSDiskSnapshotName) - stateBag.Put(constants.ArmManagedImageDataDiskSnapshotPrefix, b.config.ManagedImageDataDiskSnapshotPrefix) - stateBag.Put(constants.ArmAsyncResourceGroupDelete, b.config.AsyncResourceGroupDelete) - stateBag.Put(constants.ArmKeepOSDisk, b.config.KeepOSDisk) - - if b.config.isManagedImage() && b.config.SharedGalleryDestination.SigDestinationGalleryName != "" { - stateBag.Put(constants.ArmManagedImageSigPublishResourceGroup, b.config.SharedGalleryDestination.SigDestinationResourceGroup) - stateBag.Put(constants.ArmManagedImageSharedGalleryName, b.config.SharedGalleryDestination.SigDestinationGalleryName) - stateBag.Put(constants.ArmManagedImageSharedGalleryImageName, b.config.SharedGalleryDestination.SigDestinationImageName) - stateBag.Put(constants.ArmManagedImageSharedGalleryImageVersion, b.config.SharedGalleryDestination.SigDestinationImageVersion) - stateBag.Put(constants.ArmManagedImageSharedGalleryImageVersionStorageAccountType, b.config.SharedGalleryDestination.SigDestinationStorageAccountType) - stateBag.Put(constants.ArmManagedImageSubscription, b.config.ClientConfig.SubscriptionID) - stateBag.Put(constants.ArmManagedImageSharedGalleryImageVersionEndOfLifeDate, b.config.SharedGalleryImageVersionEndOfLifeDate) - stateBag.Put(constants.ArmManagedImageSharedGalleryImageVersionReplicaCount, b.config.SharedGalleryImageVersionReplicaCount) - stateBag.Put(constants.ArmManagedImageSharedGalleryImageVersionExcludeFromLatest, b.config.SharedGalleryImageVersionExcludeFromLatest) - } -} - -// Parameters that are only known at runtime after querying Azure. -func (b *Builder) setRuntimeParameters(stateBag multistep.StateBag) { - stateBag.Put(constants.ArmLocation, b.config.Location) -} - -func (b *Builder) setTemplateParameters(stateBag multistep.StateBag) { - stateBag.Put(constants.ArmVirtualMachineCaptureParameters, b.config.toVirtualMachineCaptureParameters()) -} - -func (b *Builder) setImageParameters(stateBag multistep.StateBag) { - stateBag.Put(constants.ArmImageParameters, b.config.toImageParameters()) -} - -func (b *Builder) getServicePrincipalTokens(say func(string)) (*adal.ServicePrincipalToken, *adal.ServicePrincipalToken, error) { - return b.config.ClientConfig.GetServicePrincipalTokens(say) -} - -func getObjectIdFromToken(ui packersdk.Ui, token *adal.ServicePrincipalToken) string { - claims := jwt.MapClaims{} - var p jwt.Parser - - var err error - - _, _, err = p.ParseUnverified(token.OAuthToken(), claims) - - if err != nil { - ui.Error(fmt.Sprintf("Failed to parse the token,Error: %s", err.Error())) - return "" - } - - oid, _ := claims["oid"].(string) - return oid -} - -func normalizeAzureRegion(name string) string { - return strings.ToLower(strings.Replace(name, " ", "", -1)) -} diff --git a/builder/azure/arm/builder_acc_test.go b/builder/azure/arm/builder_acc_test.go deleted file mode 100644 index d9a48117a..000000000 --- a/builder/azure/arm/builder_acc_test.go +++ /dev/null @@ -1,513 +0,0 @@ -package arm - -// these tests require the following variables to be set, -// although some test will only use a subset: -// -// * ARM_CLIENT_ID -// * ARM_CLIENT_SECRET -// * ARM_SUBSCRIPTION_ID -// * ARM_STORAGE_ACCOUNT -// -// The subscription in question should have a resource group -// called "packer-acceptance-test" in "South Central US" region. The -// storage account referred to in the above variable should -// be inside this resource group and in "South Central US" as well. -// -// In addition, the PACKER_ACC variable should also be set to -// a non-empty value to enable Packer acceptance tests and the -// options "-v -timeout 90m" should be provided to the test -// command, e.g.: -// go test -v -timeout 90m -run TestBuilderAcc_.* - -import ( - "bytes" - "context" - "errors" - "fmt" - "os" - "strings" - "testing" - - "github.com/Azure/go-autorest/autorest" - "github.com/Azure/go-autorest/autorest/azure/auth" - packersdk "github.com/hashicorp/packer-plugin-sdk/packer" - builderT "github.com/hashicorp/packer/acctest" -) - -const DeviceLoginAcceptanceTest = "DEVICELOGIN_TEST" - -func TestBuilderAcc_ManagedDisk_Windows(t *testing.T) { - builderT.Test(t, builderT.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Builder: &Builder{}, - Template: testBuilderAccManagedDiskWindows, - }) -} - -func TestBuilderAcc_ManagedDisk_Windows_Build_Resource_Group(t *testing.T) { - builderT.Test(t, builderT.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Builder: &Builder{}, - Template: testBuilderAccManagedDiskWindowsBuildResourceGroup, - }) -} - -func TestBuilderAcc_ManagedDisk_Windows_Build_Resource_Group_Additional_Disk(t *testing.T) { - builderT.Test(t, builderT.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Builder: &Builder{}, - Template: testBuilderAccManagedDiskWindowsBuildResourceGroupAdditionalDisk, - }) -} - -func TestBuilderAcc_ManagedDisk_Windows_DeviceLogin(t *testing.T) { - if os.Getenv(DeviceLoginAcceptanceTest) == "" { - t.Skip(fmt.Sprintf( - "Device Login Acceptance tests skipped unless env '%s' set, as its requires manual step during execution", - DeviceLoginAcceptanceTest)) - return - } - builderT.Test(t, builderT.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Builder: &Builder{}, - Template: testBuilderAccManagedDiskWindowsDeviceLogin, - }) -} - -func TestBuilderAcc_ManagedDisk_Linux(t *testing.T) { - builderT.Test(t, builderT.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Builder: &Builder{}, - Template: testBuilderAccManagedDiskLinux, - }) -} - -func TestBuilderAcc_ManagedDisk_Linux_DeviceLogin(t *testing.T) { - if os.Getenv(DeviceLoginAcceptanceTest) == "" { - t.Skip(fmt.Sprintf( - "Device Login Acceptance tests skipped unless env '%s' set, as its requires manual step during execution", - DeviceLoginAcceptanceTest)) - return - } - builderT.Test(t, builderT.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Builder: &Builder{}, - Template: testBuilderAccManagedDiskLinuxDeviceLogin, - }) -} - -func TestBuilderAcc_ManagedDisk_Linux_AzureCLI(t *testing.T) { - if os.Getenv("AZURE_CLI_AUTH") == "" { - t.Skip("Azure CLI Acceptance tests skipped unless env 'AZURE_CLI_AUTH' is set, and an active `az login` session has been established") - return - } - - var b Builder - builderT.Test(t, builderT.TestCase{ - PreCheck: func() { testAuthPreCheck(t) }, - Builder: &b, - Template: testBuilderAccManagedDiskLinuxAzureCLI, - Check: func([]packersdk.Artifact) error { - checkTemporaryGroupDeleted(t, &b) - return nil - }, - }) -} - -func TestBuilderAcc_Blob_Windows(t *testing.T) { - builderT.Test(t, builderT.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Builder: &Builder{}, - Template: testBuilderAccBlobWindows, - }) -} - -func TestBuilderAcc_Blob_Linux(t *testing.T) { - var b Builder - builderT.Test(t, builderT.TestCase{ - PreCheck: func() { testAuthPreCheck(t) }, - Builder: &b, - Template: testBuilderAccBlobLinux, - Check: func([]packersdk.Artifact) error { - checkUnmanagedVHDDeleted(t, &b) - return nil - }, - }) -} - -func testAccPreCheck(*testing.T) {} - -func testAuthPreCheck(t *testing.T) { - _, err := auth.NewAuthorizerFromEnvironment() - if err != nil { - t.Fatalf("failed to auth to azure: %s", err) - } -} - -func checkTemporaryGroupDeleted(t *testing.T, b *Builder) { - ui := testUi() - - spnCloud, spnKeyVault, err := b.getServicePrincipalTokens(ui.Say) - if err != nil { - t.Fatalf("failed getting azure tokens: %s", err) - } - - ui.Message("Creating test Azure Resource Manager (ARM) client ...") - azureClient, err := NewAzureClient( - b.config.ClientConfig.SubscriptionID, - b.config.SharedGalleryDestination.SigDestinationSubscription, - b.config.ResourceGroupName, - b.config.StorageAccount, - b.config.ClientConfig.CloudEnvironment(), - b.config.SharedGalleryTimeout, - b.config.PollingDurationTimeout, - spnCloud, - spnKeyVault) - - if err != nil { - t.Fatalf("failed to create azure client: %s", err) - } - - // Validate resource group has been deleted - _, err = azureClient.GroupsClient.Get(context.Background(), b.config.tmpResourceGroupName) - if err == nil || !resourceNotFound(err) { - t.Fatalf("failed validating resource group deletion: %s", err) - } -} - -func checkUnmanagedVHDDeleted(t *testing.T, b *Builder) { - ui := testUi() - - spnCloud, spnKeyVault, err := b.getServicePrincipalTokens(ui.Say) - if err != nil { - t.Fatalf("failed getting azure tokens: %s", err) - } - - azureClient, err := NewAzureClient( - b.config.ClientConfig.SubscriptionID, - b.config.SharedGalleryDestination.SigDestinationSubscription, - b.config.ResourceGroupName, - b.config.StorageAccount, - b.config.ClientConfig.CloudEnvironment(), - b.config.SharedGalleryTimeout, - b.config.PollingDurationTimeout, - spnCloud, - spnKeyVault) - - if err != nil { - t.Fatalf("failed to create azure client: %s", err) - } - - // validate temporary os blob was deleted - blob := azureClient.BlobStorageClient.GetContainerReference("images").GetBlobReference(b.config.tmpOSDiskName) - _, err = blob.BreakLease(nil) - if err != nil && !strings.Contains(err.Error(), "BlobNotFound") { - t.Fatalf("failed validating deletion of unmanaged vhd: %s", err) - } - - // Validate resource group has been deleted - _, err = azureClient.GroupsClient.Get(context.Background(), b.config.tmpResourceGroupName) - if err == nil || !resourceNotFound(err) { - t.Fatalf("failed validating resource group deletion: %s", err) - } -} - -func resourceNotFound(err error) bool { - derr := autorest.DetailedError{} - return errors.As(err, &derr) && derr.StatusCode == 404 -} - -func testUi() *packersdk.BasicUi { - return &packersdk.BasicUi{ - Reader: new(bytes.Buffer), - Writer: new(bytes.Buffer), - ErrorWriter: new(bytes.Buffer), - } -} - -const testBuilderAccManagedDiskWindows = ` -{ - "variables": { - "client_id": "{{env ` + "`ARM_CLIENT_ID`" + `}}", - "client_secret": "{{env ` + "`ARM_CLIENT_SECRET`" + `}}", - "subscription_id": "{{env ` + "`ARM_SUBSCRIPTION_ID`" + `}}" - }, - "builders": [{ - "type": "test", - - "client_id": "{{user ` + "`client_id`" + `}}", - "client_secret": "{{user ` + "`client_secret`" + `}}", - "subscription_id": "{{user ` + "`subscription_id`" + `}}", - - "managed_image_resource_group_name": "packer-acceptance-test", - "managed_image_name": "testBuilderAccManagedDiskWindows-{{timestamp}}", - - "os_type": "Windows", - "image_publisher": "MicrosoftWindowsServer", - "image_offer": "WindowsServer", - "image_sku": "2012-R2-Datacenter", - - "communicator": "winrm", - "winrm_use_ssl": "true", - "winrm_insecure": "true", - "winrm_timeout": "3m", - "winrm_username": "packer", - "async_resourcegroup_delete": "true", - - "location": "South Central US", - "vm_size": "Standard_DS2_v2" - }] -} -` - -const testBuilderAccManagedDiskWindowsBuildResourceGroup = ` -{ - "variables": { - "client_id": "{{env ` + "`ARM_CLIENT_ID`" + `}}", - "client_secret": "{{env ` + "`ARM_CLIENT_SECRET`" + `}}", - "subscription_id": "{{env ` + "`ARM_SUBSCRIPTION_ID`" + `}}" - }, - "builders": [{ - "type": "test", - - "client_id": "{{user ` + "`client_id`" + `}}", - "client_secret": "{{user ` + "`client_secret`" + `}}", - "subscription_id": "{{user ` + "`subscription_id`" + `}}", - - "build_resource_group_name" : "packer-acceptance-test", - "managed_image_resource_group_name": "packer-acceptance-test", - "managed_image_name": "testBuilderAccManagedDiskWindowsBuildResourceGroup-{{timestamp}}", - - "os_type": "Windows", - "image_publisher": "MicrosoftWindowsServer", - "image_offer": "WindowsServer", - "image_sku": "2012-R2-Datacenter", - - "communicator": "winrm", - "winrm_use_ssl": "true", - "winrm_insecure": "true", - "winrm_timeout": "3m", - "winrm_username": "packer", - "async_resourcegroup_delete": "true", - - "vm_size": "Standard_DS2_v2" - }] -} -` - -const testBuilderAccManagedDiskWindowsBuildResourceGroupAdditionalDisk = ` -{ - "variables": { - "client_id": "{{env ` + "`ARM_CLIENT_ID`" + `}}", - "client_secret": "{{env ` + "`ARM_CLIENT_SECRET`" + `}}", - "subscription_id": "{{env ` + "`ARM_SUBSCRIPTION_ID`" + `}}" - }, - "builders": [{ - "type": "test", - - "client_id": "{{user ` + "`client_id`" + `}}", - "client_secret": "{{user ` + "`client_secret`" + `}}", - "subscription_id": "{{user ` + "`subscription_id`" + `}}", - - "build_resource_group_name" : "packer-acceptance-test", - "managed_image_resource_group_name": "packer-acceptance-test", - "managed_image_name": "testBuilderAccManagedDiskWindowsBuildResourceGroupAdditionDisk-{{timestamp}}", - - "os_type": "Windows", - "image_publisher": "MicrosoftWindowsServer", - "image_offer": "WindowsServer", - "image_sku": "2012-R2-Datacenter", - - "communicator": "winrm", - "winrm_use_ssl": "true", - "winrm_insecure": "true", - "winrm_timeout": "3m", - "winrm_username": "packer", - "async_resourcegroup_delete": "true", - - "vm_size": "Standard_DS2_v2", - "disk_additional_size": [10,15] - }] -} -` - -const testBuilderAccManagedDiskWindowsDeviceLogin = ` -{ - "variables": { - "subscription_id": "{{env ` + "`ARM_SUBSCRIPTION_ID`" + `}}" - }, - "builders": [{ - "type": "test", - - "subscription_id": "{{user ` + "`subscription_id`" + `}}", - - "managed_image_resource_group_name": "packer-acceptance-test", - "managed_image_name": "testBuilderAccManagedDiskWindowsDeviceLogin-{{timestamp}}", - - "os_type": "Windows", - "image_publisher": "MicrosoftWindowsServer", - "image_offer": "WindowsServer", - "image_sku": "2012-R2-Datacenter", - - "communicator": "winrm", - "winrm_use_ssl": "true", - "winrm_insecure": "true", - "winrm_timeout": "3m", - "winrm_username": "packer", - - "location": "South Central US", - "vm_size": "Standard_DS2_v2" - }] -} -` - -const testBuilderAccManagedDiskLinux = ` -{ - "variables": { - "client_id": "{{env ` + "`ARM_CLIENT_ID`" + `}}", - "client_secret": "{{env ` + "`ARM_CLIENT_SECRET`" + `}}", - "subscription_id": "{{env ` + "`ARM_SUBSCRIPTION_ID`" + `}}" - }, - "builders": [{ - "type": "test", - - "client_id": "{{user ` + "`client_id`" + `}}", - "client_secret": "{{user ` + "`client_secret`" + `}}", - "subscription_id": "{{user ` + "`subscription_id`" + `}}", - - "managed_image_resource_group_name": "packer-acceptance-test", - "managed_image_name": "testBuilderAccManagedDiskLinux-{{timestamp}}", - - "os_type": "Linux", - "image_publisher": "Canonical", - "image_offer": "UbuntuServer", - "image_sku": "16.04-LTS", - - "location": "South Central US", - "vm_size": "Standard_DS2_v2", - "azure_tags": { - "env": "testing", - "builder": "packer" - } - }] -} -` - -const testBuilderAccManagedDiskLinuxDeviceLogin = ` -{ - "variables": { - "subscription_id": "{{env ` + "`ARM_SUBSCRIPTION_ID`" + `}}" - }, - "builders": [{ - "type": "test", - - "subscription_id": "{{user ` + "`subscription_id`" + `}}", - - "managed_image_resource_group_name": "packer-acceptance-test", - "managed_image_name": "testBuilderAccManagedDiskLinuxDeviceLogin-{{timestamp}}", - - "os_type": "Linux", - "image_publisher": "Canonical", - "image_offer": "UbuntuServer", - "image_sku": "16.04-LTS", - "async_resourcegroup_delete": "true", - - "location": "South Central US", - "vm_size": "Standard_DS2_v2" - }] -} -` - -const testBuilderAccBlobWindows = ` -{ - "variables": { - "client_id": "{{env ` + "`ARM_CLIENT_ID`" + `}}", - "client_secret": "{{env ` + "`ARM_CLIENT_SECRET`" + `}}", - "subscription_id": "{{env ` + "`ARM_SUBSCRIPTION_ID`" + `}}", - "storage_account": "{{env ` + "`ARM_STORAGE_ACCOUNT`" + `}}" - }, - "builders": [{ - "type": "test", - - "client_id": "{{user ` + "`client_id`" + `}}", - "client_secret": "{{user ` + "`client_secret`" + `}}", - "subscription_id": "{{user ` + "`subscription_id`" + `}}", - - "storage_account": "{{user ` + "`storage_account`" + `}}", - "resource_group_name": "packer-acceptance-test", - "capture_container_name": "test", - "capture_name_prefix": "testBuilderAccBlobWin", - - "os_type": "Windows", - "image_publisher": "MicrosoftWindowsServer", - "image_offer": "WindowsServer", - "image_sku": "2012-R2-Datacenter", - - "communicator": "winrm", - "winrm_use_ssl": "true", - "winrm_insecure": "true", - "winrm_timeout": "3m", - "winrm_username": "packer", - - "location": "South Central US", - "vm_size": "Standard_DS2_v2" - }] -} -` - -const testBuilderAccBlobLinux = ` -{ - "variables": { - "client_id": "{{env ` + "`ARM_CLIENT_ID`" + `}}", - "client_secret": "{{env ` + "`ARM_CLIENT_SECRET`" + `}}", - "subscription_id": "{{env ` + "`ARM_SUBSCRIPTION_ID`" + `}}", - "storage_account": "{{env ` + "`ARM_STORAGE_ACCOUNT`" + `}}" - }, - "builders": [{ - "type": "test", - - "client_id": "{{user ` + "`client_id`" + `}}", - "client_secret": "{{user ` + "`client_secret`" + `}}", - "subscription_id": "{{user ` + "`subscription_id`" + `}}", - - "storage_account": "{{user ` + "`storage_account`" + `}}", - "resource_group_name": "packer-acceptance-test", - "capture_container_name": "test", - "capture_name_prefix": "testBuilderAccBlobLinux", - - "os_type": "Linux", - "image_publisher": "Canonical", - "image_offer": "UbuntuServer", - "image_sku": "16.04-LTS", - - "location": "South Central US", - "vm_size": "Standard_DS2_v2" - }] -} -` - -const testBuilderAccManagedDiskLinuxAzureCLI = ` -{ - "builders": [{ - "type": "test", - - "use_azure_cli_auth": true, - - "managed_image_resource_group_name": "packer-acceptance-test", - "managed_image_name": "testBuilderAccManagedDiskLinuxAzureCLI-{{timestamp}}", - "temp_resource_group_name": "packer-acceptance-test-managed-cli", - - "os_type": "Linux", - "image_publisher": "Canonical", - "image_offer": "UbuntuServer", - "image_sku": "16.04-LTS", - - "location": "South Central US", - "vm_size": "Standard_DS2_v2", - "azure_tags": { - "env": "testing", - "builder": "packer" - } - }] -} -` diff --git a/builder/azure/arm/builder_test.go b/builder/azure/arm/builder_test.go deleted file mode 100644 index 3f4bb77c0..000000000 --- a/builder/azure/arm/builder_test.go +++ /dev/null @@ -1,67 +0,0 @@ -package arm - -import ( - "testing" - - "github.com/hashicorp/packer/builder/azure/common/constants" -) - -func TestStateBagShouldBePopulatedExpectedValues(t *testing.T) { - var testSubject Builder - _, _, err := testSubject.Prepare(getArmBuilderConfiguration(), getPackerConfiguration()) - if err != nil { - t.Fatalf("failed to prepare: %s", err) - } - - var expectedStateBagKeys = []string{ - constants.AuthorizedKey, - - constants.ArmTags, - constants.ArmComputeName, - constants.ArmDeploymentName, - constants.ArmNicName, - constants.ArmResourceGroupName, - constants.ArmStorageAccountName, - constants.ArmVirtualMachineCaptureParameters, - constants.ArmPublicIPAddressName, - constants.ArmAsyncResourceGroupDelete, - } - - for _, v := range expectedStateBagKeys { - if _, ok := testSubject.stateBag.GetOk(v); ok == false { - t.Errorf("Expected the builder's state bag to contain '%s', but it did not.", v) - } - } -} - -func TestStateBagShouldPoluateExpectedTags(t *testing.T) { - var testSubject Builder - - expectedTags := map[string]string{ - "env": "test", - "builder": "packer", - } - armConfig := getArmBuilderConfiguration() - armConfig["azure_tags"] = expectedTags - - _, _, err := testSubject.Prepare(armConfig, getPackerConfiguration()) - if err != nil { - t.Fatalf("failed to prepare: %s", err) - } - - tags, ok := testSubject.stateBag.Get(constants.ArmTags).(map[string]*string) - if !ok { - t.Errorf("Expected the builder's state bag to contain tags of type %T, but didn't.", testSubject.config.AzureTags) - } - - if len(tags) != len(expectedTags) { - t.Errorf("expect tags from state to be the same length as tags from config") - } - - for k, v := range tags { - if expectedTags[k] != *v { - t.Errorf("expect tag value of %s to be %s, but got %s", k, expectedTags[k], *v) - } - } - -} diff --git a/builder/azure/arm/capture_template.go b/builder/azure/arm/capture_template.go deleted file mode 100644 index 2ba2c48b8..000000000 --- a/builder/azure/arm/capture_template.go +++ /dev/null @@ -1,84 +0,0 @@ -package arm - -type CaptureTemplateParameter struct { - Type string `json:"type"` - DefaultValue string `json:"defaultValue,omitempty"` -} - -type CaptureHardwareProfile struct { - VMSize string `json:"vmSize"` -} - -type CaptureUri struct { - Uri string `json:"uri"` -} - -type CaptureDisk struct { - OSType string `json:"osType"` - Name string `json:"name"` - Image CaptureUri `json:"image"` - Vhd CaptureUri `json:"vhd"` - CreateOption string `json:"createOption"` - Caching string `json:"caching"` -} - -type CaptureStorageProfile struct { - OSDisk CaptureDisk `json:"osDisk"` - DataDisks []CaptureDisk `json:"dataDisks"` -} - -type CaptureOSProfile struct { - ComputerName string `json:"computerName"` - AdminUsername string `json:"adminUsername"` - AdminPassword string `json:"adminPassword"` -} - -type CaptureNetworkInterface struct { - Id string `json:"id"` -} - -type CaptureNetworkProfile struct { - NetworkInterfaces []CaptureNetworkInterface `json:"networkInterfaces"` -} - -type CaptureBootDiagnostics struct { - Enabled bool `json:"enabled"` -} - -type CaptureDiagnosticProfile struct { - BootDiagnostics CaptureBootDiagnostics `json:"bootDiagnostics"` -} - -type CaptureProperties struct { - HardwareProfile CaptureHardwareProfile `json:"hardwareProfile"` - StorageProfile CaptureStorageProfile `json:"storageProfile"` - OSProfile CaptureOSProfile `json:"osProfile"` - NetworkProfile CaptureNetworkProfile `json:"networkProfile"` - DiagnosticsProfile CaptureDiagnosticProfile `json:"diagnosticsProfile"` - ProvisioningState int `json:"provisioningState"` -} - -type CaptureResources struct { - ApiVersion string `json:"apiVersion"` - Name string `json:"name"` - Type string `json:"type"` - Location string `json:"location"` - Properties CaptureProperties `json:"properties"` -} - -type CaptureTemplate struct { - Schema string `json:"$schema"` - ContentVersion string `json:"contentVersion"` - Parameters map[string]CaptureTemplateParameter `json:"parameters"` - Resources []CaptureResources `json:"resources"` -} - -type CaptureOperationProperties struct { - Output *CaptureTemplate `json:"output"` -} - -type CaptureOperation struct { - OperationId string `json:"operationId"` - Status string `json:"status"` - Properties *CaptureOperationProperties `json:"properties"` -} diff --git a/builder/azure/arm/capture_template_test.go b/builder/azure/arm/capture_template_test.go deleted file mode 100644 index 10be51ee8..000000000 --- a/builder/azure/arm/capture_template_test.go +++ /dev/null @@ -1,272 +0,0 @@ -package arm - -import ( - "encoding/json" - "testing" -) - -var captureTemplate01 = `{ - "operationId": "ac1c7c38-a591-41b3-89bd-ea39fceace1b", - "status": "Succeeded", - "startTime": "2016-04-04T21:07:25.2900874+00:00", - "endTime": "2016-04-04T21:07:26.4776321+00:00", - "properties": { - "output": { - "$schema": "http://schema.management.azure.com/schemas/2014-04-01-preview/VM_IP.json", - "contentVersion": "1.0.0.0", - "parameters": { - "vmName": { - "type": "string" - }, - "vmSize": { - "type": "string", - "defaultValue": "Standard_A2" - }, - "adminUserName": { - "type": "string" - }, - "adminPassword": { - "type": "securestring" - }, - "networkInterfaceId": { - "type": "string" - } - }, - "resources": [ - { - "apiVersion": "2015-06-15", - "properties": { - "hardwareProfile": { - "vmSize": "[parameters('vmSize')]" - }, - "storageProfile": { - "osDisk": { - "osType": "Linux", - "name": "packer-osDisk.32118633-6dc9-449f-83b6-a7d2983bec14.vhd", - "createOption": "FromImage", - "image": { - "uri": "http://storage.blob.core.windows.net/system/Microsoft.Compute/Images/images/packer-osDisk.32118633-6dc9-449f-83b6-a7d2983bec14.vhd" - }, - "vhd": { - "uri": "http://storage.blob.core.windows.net/vmcontainerce1a1b75-f480-47cb-8e6e-55142e4a5f68/osDisk.ce1a1b75-f480-47cb-8e6e-55142e4a5f68.vhd" - }, - "caching": "ReadWrite" - }, - "dataDisks": [ - { - "lun": 0, - "name": "packer-datadisk-0.32118633-6dc9-449f-83b6-a7d2983bec14.vhd", - "createOption": "Empty", - "image": { - "uri": "http://storage.blob.core.windows.net/system/Microsoft.Compute/Images/images/packer-datadisk-0.32118633-6dc9-449f-83b6-a7d2983bec14.vhd" - }, - "vhd": { - "uri": "http://storage.blob.core.windows.net/vmcontainerce1a1b75-f480-47cb-8e6e-55142e4a5f68/datadisk-0.ce1a1b75-f480-47cb-8e6e-55142e4a5f68.vhd" - }, - "caching": "ReadWrite" - }, - { - "lun": 1, - "name": "packer-datadisk-1.32118633-6dc9-449f-83b6-a7d2983bec14.vhd", - "createOption": "Empty", - "image": { - "uri": "http://storage.blob.core.windows.net/system/Microsoft.Compute/Images/images/packer-datadisk-1.32118633-6dc9-449f-83b6-a7d2983bec14.vhd" - }, - "vhd": { - "uri": "http://storage.blob.core.windows.net/vmcontainerce1a1b75-f480-47cb-8e6e-55142e4a5f68/datadisk-1.ce1a1b75-f480-47cb-8e6e-55142e4a5f68.vhd" - }, - "caching": "ReadWrite" - } - ] - }, - "osProfile": { - "computerName": "[parameters('vmName')]", - "adminUsername": "[parameters('adminUsername')]", - "adminPassword": "[parameters('adminPassword')]" - }, - "networkProfile": { - "networkInterfaces": [ - { - "id": "[parameters('networkInterfaceId')]" - } - ] - }, - "diagnosticsProfile": { - "bootDiagnostics": { - "enabled": false - } - }, - "provisioningState": 0 - }, - "name": "[parameters('vmName')]", - "type": "Microsoft.Compute/virtualMachines", - "location": "southcentralus" - } - ] - } - } -}` - -var captureTemplate02 = `{ - "operationId": "ac1c7c38-a591-41b3-89bd-ea39fceace1b", - "status": "Succeeded", - "startTime": "2016-04-04T21:07:25.2900874+00:00", - "endTime": "2016-04-04T21:07:26.4776321+00:00" -}` - -func TestCaptureParseJson(t *testing.T) { - var operation CaptureOperation - err := json.Unmarshal([]byte(captureTemplate01), &operation) - if err != nil { - t.Fatalf("failed to the sample capture operation: %s", err) - } - - testSubject := operation.Properties.Output - if testSubject.Schema != "http://schema.management.azure.com/schemas/2014-04-01-preview/VM_IP.json" { - t.Errorf("Schema's value was unexpected: %s", testSubject.Schema) - } - if testSubject.ContentVersion != "1.0.0.0" { - t.Errorf("ContentVersion's value was unexpected: %s", testSubject.ContentVersion) - } - - // == Parameters ==================================== - if len(testSubject.Parameters) != 5 { - t.Fatalf("expected parameters to have 5 keys, but got %d", len(testSubject.Parameters)) - } - if _, ok := testSubject.Parameters["vmName"]; !ok { - t.Errorf("Parameters['vmName'] was an expected parameters, but it did not exist") - } - if testSubject.Parameters["vmName"].Type != "string" { - t.Errorf("Parameters['vmName'].Type == 'string', but got '%s'", testSubject.Parameters["vmName"].Type) - } - if _, ok := testSubject.Parameters["vmSize"]; !ok { - t.Errorf("Parameters['vmSize'] was an expected parameters, but it did not exist") - } - if testSubject.Parameters["vmSize"].Type != "string" { - t.Errorf("Parameters['vmSize'].Type == 'string', but got '%s'", testSubject.Parameters["vmSize"]) - } - if testSubject.Parameters["vmSize"].DefaultValue != "Standard_A2" { - t.Errorf("Parameters['vmSize'].DefaultValue == 'string', but got '%s'", testSubject.Parameters["vmSize"].DefaultValue) - } - - // == Resources ===================================== - if len(testSubject.Resources) != 1 { - t.Fatalf("expected resources to have length 1, but got %d", len(testSubject.Resources)) - } - if testSubject.Resources[0].Name != "[parameters('vmName')]" { - t.Errorf("Resources[0].Name's value was unexpected: %s", testSubject.Resources[0].Name) - } - if testSubject.Resources[0].Type != "Microsoft.Compute/virtualMachines" { - t.Errorf("Resources[0].Type's value was unexpected: %s", testSubject.Resources[0].Type) - } - if testSubject.Resources[0].Location != "southcentralus" { - t.Errorf("Resources[0].Location's value was unexpected: %s", testSubject.Resources[0].Location) - } - - // == Resources/Properties ===================================== - if testSubject.Resources[0].Properties.ProvisioningState != 0 { - t.Errorf("Resources[0].Properties.ProvisioningState's value was unexpected: %d", testSubject.Resources[0].Properties.ProvisioningState) - } - - // == Resources/Properties/HardwareProfile ====================== - hardwareProfile := testSubject.Resources[0].Properties.HardwareProfile - if hardwareProfile.VMSize != "[parameters('vmSize')]" { - t.Errorf("Resources[0].Properties.HardwareProfile.VMSize's value was unexpected: %s", hardwareProfile.VMSize) - } - - // == Resources/Properties/StorageProfile/OSDisk ================ - osDisk := testSubject.Resources[0].Properties.StorageProfile.OSDisk - if osDisk.OSType != "Linux" { - t.Errorf("Resources[0].Properties.StorageProfile.OSDisk.OSDisk's value was unexpected: %s", osDisk.OSType) - } - if osDisk.Name != "packer-osDisk.32118633-6dc9-449f-83b6-a7d2983bec14.vhd" { - t.Errorf("Resources[0].Properties.StorageProfile.OSDisk.Name's value was unexpected: %s", osDisk.Name) - } - if osDisk.CreateOption != "FromImage" { - t.Errorf("Resources[0].Properties.StorageProfile.OSDisk.CreateOption's value was unexpected: %s", osDisk.CreateOption) - } - if osDisk.Image.Uri != "http://storage.blob.core.windows.net/system/Microsoft.Compute/Images/images/packer-osDisk.32118633-6dc9-449f-83b6-a7d2983bec14.vhd" { - t.Errorf("Resources[0].Properties.StorageProfile.OSDisk.Image.Uri's value was unexpected: %s", osDisk.Image.Uri) - } - if osDisk.Vhd.Uri != "http://storage.blob.core.windows.net/vmcontainerce1a1b75-f480-47cb-8e6e-55142e4a5f68/osDisk.ce1a1b75-f480-47cb-8e6e-55142e4a5f68.vhd" { - t.Errorf("Resources[0].Properties.StorageProfile.OSDisk.Vhd.Uri's value was unexpected: %s", osDisk.Vhd.Uri) - } - if osDisk.Caching != "ReadWrite" { - t.Errorf("Resources[0].Properties.StorageProfile.OSDisk.Caching's value was unexpected: %s", osDisk.Caching) - } - - // == Resources/Properties/StorageProfile/DataDisks ================ - dataDisks := testSubject.Resources[0].Properties.StorageProfile.DataDisks - if len(dataDisks) != 2 { - t.Errorf("Resources[0].Properties.StorageProfile.DataDisks, 2 disks expected but was: %d", len(dataDisks)) - } - if dataDisks[0].Name != "packer-datadisk-0.32118633-6dc9-449f-83b6-a7d2983bec14.vhd" { - t.Errorf("Resources[0].Properties.StorageProfile.dataDisks[0].Name's value was unexpected: %s", dataDisks[0].Name) - } - if dataDisks[0].CreateOption != "Empty" { - t.Errorf("Resources[0].Properties.StorageProfile.dataDisks[0].CreateOption's value was unexpected: %s", dataDisks[0].CreateOption) - } - if dataDisks[0].Image.Uri != "http://storage.blob.core.windows.net/system/Microsoft.Compute/Images/images/packer-datadisk-0.32118633-6dc9-449f-83b6-a7d2983bec14.vhd" { - t.Errorf("Resources[0].Properties.StorageProfile.dataDisks[0].Image.Uri's value was unexpected: %s", dataDisks[0].Image.Uri) - } - if dataDisks[0].Vhd.Uri != "http://storage.blob.core.windows.net/vmcontainerce1a1b75-f480-47cb-8e6e-55142e4a5f68/datadisk-0.ce1a1b75-f480-47cb-8e6e-55142e4a5f68.vhd" { - t.Errorf("Resources[0].Properties.StorageProfile.dataDisks[0].Vhd.Uri's value was unexpected: %s", dataDisks[0].Vhd.Uri) - } - if dataDisks[0].Caching != "ReadWrite" { - t.Errorf("Resources[0].Properties.StorageProfile.dataDisks[0].Caching's value was unexpected: %s", dataDisks[0].Caching) - } - if dataDisks[1].Name != "packer-datadisk-1.32118633-6dc9-449f-83b6-a7d2983bec14.vhd" { - t.Errorf("Resources[0].Properties.StorageProfile.dataDisks[1].Name's value was unexpected: %s", dataDisks[1].Name) - } - if dataDisks[1].CreateOption != "Empty" { - t.Errorf("Resources[0].Properties.StorageProfile.dataDisks[1].CreateOption's value was unexpected: %s", dataDisks[1].CreateOption) - } - if dataDisks[1].Image.Uri != "http://storage.blob.core.windows.net/system/Microsoft.Compute/Images/images/packer-datadisk-1.32118633-6dc9-449f-83b6-a7d2983bec14.vhd" { - t.Errorf("Resources[0].Properties.StorageProfile.dataDisks[1].Image.Uri's value was unexpected: %s", dataDisks[1].Image.Uri) - } - if dataDisks[1].Vhd.Uri != "http://storage.blob.core.windows.net/vmcontainerce1a1b75-f480-47cb-8e6e-55142e4a5f68/datadisk-1.ce1a1b75-f480-47cb-8e6e-55142e4a5f68.vhd" { - t.Errorf("Resources[0].Properties.StorageProfile.dataDisks[1].Vhd.Uri's value was unexpected: %s", dataDisks[1].Vhd.Uri) - } - if dataDisks[1].Caching != "ReadWrite" { - t.Errorf("Resources[0].Properties.StorageProfile.dataDisks[1].Caching's value was unexpected: %s", dataDisks[1].Caching) - } - - // == Resources/Properties/OSProfile ============================ - osProfile := testSubject.Resources[0].Properties.OSProfile - if osProfile.AdminPassword != "[parameters('adminPassword')]" { - t.Errorf("Resources[0].Properties.OSProfile.AdminPassword's value was unexpected: %s", osProfile.AdminPassword) - } - if osProfile.AdminUsername != "[parameters('adminUsername')]" { - t.Errorf("Resources[0].Properties.OSProfile.AdminUsername's value was unexpected: %s", osProfile.AdminUsername) - } - if osProfile.ComputerName != "[parameters('vmName')]" { - t.Errorf("Resources[0].Properties.OSProfile.ComputerName's value was unexpected: %s", osProfile.ComputerName) - } - - // == Resources/Properties/NetworkProfile ======================= - networkProfile := testSubject.Resources[0].Properties.NetworkProfile - if len(networkProfile.NetworkInterfaces) != 1 { - t.Errorf("Count of Resources[0].Properties.NetworkProfile.NetworkInterfaces was expected to be 1, but go %d", len(networkProfile.NetworkInterfaces)) - } - if networkProfile.NetworkInterfaces[0].Id != "[parameters('networkInterfaceId')]" { - t.Errorf("Resources[0].Properties.NetworkProfile.NetworkInterfaces[0].Id's value was unexpected: %s", networkProfile.NetworkInterfaces[0].Id) - } - - // == Resources/Properties/DiagnosticsProfile =================== - diagnosticsProfile := testSubject.Resources[0].Properties.DiagnosticsProfile - if diagnosticsProfile.BootDiagnostics.Enabled != false { - t.Errorf("Resources[0].Properties.DiagnosticsProfile.BootDiagnostics.Enabled's value was unexpected: %t", diagnosticsProfile.BootDiagnostics.Enabled) - } -} - -func TestCaptureEmptyOperationJson(t *testing.T) { - var operation CaptureOperation - err := json.Unmarshal([]byte(captureTemplate02), &operation) - if err != nil { - t.Fatalf("failed to the sample capture operation: %s", err) - } - - if operation.Properties != nil { - t.Errorf("JSON contained no properties, but value was not nil: %+v", operation.Properties) - } -} diff --git a/builder/azure/arm/config.go b/builder/azure/arm/config.go deleted file mode 100644 index 6f35a2035..000000000 --- a/builder/azure/arm/config.go +++ /dev/null @@ -1,1252 +0,0 @@ -//go:generate packer-sdc struct-markdown -//go:generate packer-sdc mapstructure-to-hcl2 -type Config,SharedImageGallery,SharedImageGalleryDestination,PlanInformation - -package arm - -import ( - "crypto/rand" - "crypto/rsa" - "crypto/x509" - "crypto/x509/pkix" - "encoding/base64" - "encoding/json" - "fmt" - "io/ioutil" - "math/big" - "net" - "regexp" - "strings" - "time" - - "github.com/hashicorp/packer-plugin-sdk/random" - - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2018-04-01/compute" - "github.com/Azure/go-autorest/autorest/to" - "github.com/masterzen/winrm" - - "github.com/hashicorp/packer-plugin-sdk/common" - "github.com/hashicorp/packer-plugin-sdk/communicator" - packersdk "github.com/hashicorp/packer-plugin-sdk/packer" - "github.com/hashicorp/packer-plugin-sdk/template/config" - "github.com/hashicorp/packer-plugin-sdk/template/interpolate" - azcommon "github.com/hashicorp/packer/builder/azure/common" - "github.com/hashicorp/packer/builder/azure/common/client" - "github.com/hashicorp/packer/builder/azure/common/constants" - "github.com/hashicorp/packer/builder/azure/pkcs12" - - "golang.org/x/crypto/ssh" -) - -const ( - DefaultImageVersion = "latest" - DefaultUserName = "packer" - DefaultPrivateVirtualNetworkWithPublicIp = false - DefaultVMSize = "Standard_A1" - DefaultKeyVaultSKU = "standard" -) - -const ( - // https://docs.microsoft.com/en-us/azure/architecture/best-practices/naming-conventions#naming-rules-and-restrictions - // Regular expressions in Go are not expressive enough, such that the regular expression returned by Azure - // can be used (no backtracking). - // - // -> ^[^_\W][\w-._]{0,79}(?.UUID.vhd` and stored in the specified capture - // container along side the OS disk. The additional disks are included in - // the deployment template `PREFIX-vmTemplate.UUID`. - // - // For Managed build the final artifacts are included in the managed image. - // The additional disk will have the same storage account type as the OS - // disk, as specified with the `managed_image_storage_account_type` - // setting. - AdditionalDiskSize []int32 `mapstructure:"disk_additional_size" required:"false"` - // Specify the disk caching type. Valid values - // are None, ReadOnly, and ReadWrite. The default value is ReadWrite. - DiskCachingType string `mapstructure:"disk_caching_type" required:"false"` - diskCachingType compute.CachingTypes - // Specify the list of IP addresses and CIDR blocks that should be - // allowed access to the VM. If provided, an Azure Network Security - // Group will be created with corresponding rules and be bound to - // the subnet of the VM. - // Providing `allowed_inbound_ip_addresses` in combination with - // `virtual_network_name` is not allowed. - AllowedInboundIpAddresses []string `mapstructure:"allowed_inbound_ip_addresses"` - - // Specify storage to store Boot Diagnostics -- Enabling this option - // will create 2 Files in the specified storage account. (serial console log & screehshot file) - // once the build is completed, it has to be removed manually. - // see [here](https://docs.microsoft.com/en-us/azure/virtual-machines/troubleshooting/boot-diagnostics) for more info - BootDiagSTGAccount string `mapstructure:"boot_diag_storage_account" required:"false"` - - // specify custom azure resource names during build limited to max 10 characters - // this will set the prefix for the resources. The actuall resource names will be - // `custom_resource_build_prefix` + resourcetype + 5 character random alphanumeric string - CustomResourcePrefix string `mapstructure:"custom_resource_build_prefix" required:"false"` - - // Runtime Values - UserName string `mapstructure-to-hcl2:",skip"` - Password string `mapstructure-to-hcl2:",skip"` - tmpAdminPassword string - tmpCertificatePassword string - tmpResourceGroupName string - tmpComputeName string - tmpNicName string - tmpPublicIPAddressName string - tmpDeploymentName string - tmpKeyVaultName string - tmpOSDiskName string - tmpDataDiskName string - tmpSubnetName string - tmpVirtualNetworkName string - tmpNsgName string - tmpWinRMCertificateUrl string - - // Authentication with the VM via SSH - sshAuthorizedKey string - - // Authentication with the VM via WinRM - winrmCertificate string - - Comm communicator.Config `mapstructure:",squash"` - ctx interpolate.Context - // If you want packer to delete the - // temporary resource group asynchronously set this value. It's a boolean - // value and defaults to false. Important Setting this true means that - // your builds are faster, however any failed deletes are not reported. - AsyncResourceGroupDelete bool `mapstructure:"async_resourcegroup_delete" required:"false"` -} - -type keyVaultCertificate struct { - Data string `json:"data"` - DataType string `json:"dataType"` - Password string `json:"password,omitempty"` -} - -func (c *Config) toVMID() string { - var resourceGroupName string - if c.tmpResourceGroupName != "" { - resourceGroupName = c.tmpResourceGroupName - } else { - resourceGroupName = c.BuildResourceGroupName - } - return fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Compute/virtualMachines/%s", c.ClientConfig.SubscriptionID, resourceGroupName, c.tmpComputeName) -} - -func (c *Config) isManagedImage() bool { - return c.ManagedImageName != "" -} - -func (c *Config) toVirtualMachineCaptureParameters() *compute.VirtualMachineCaptureParameters { - return &compute.VirtualMachineCaptureParameters{ - DestinationContainerName: &c.CaptureContainerName, - VhdPrefix: &c.CaptureNamePrefix, - OverwriteVhds: to.BoolPtr(false), - } -} - -func (c *Config) toImageParameters() *compute.Image { - return &compute.Image{ - ImageProperties: &compute.ImageProperties{ - SourceVirtualMachine: &compute.SubResource{ - ID: to.StringPtr(c.toVMID()), - }, - StorageProfile: &compute.ImageStorageProfile{ - ZoneResilient: to.BoolPtr(c.ManagedImageZoneResilient), - }, - }, - Location: to.StringPtr(c.Location), - Tags: azcommon.MapToAzureTags(c.AzureTags), - } -} - -func (c *Config) createCertificate() (string, error) { - privateKey, err := rsa.GenerateKey(rand.Reader, 2048) - if err != nil { - err = fmt.Errorf("Failed to Generate Private Key: %s", err) - return "", err - } - - host := fmt.Sprintf("%s.cloudapp.net", c.tmpComputeName) - notBefore := time.Now() - notAfter := notBefore.Add(24 * time.Hour) - - serialNumber, err := rand.Int(rand.Reader, new(big.Int).Lsh(big.NewInt(1), 128)) - if err != nil { - err = fmt.Errorf("Failed to Generate Serial Number: %v", err) - return "", err - } - - template := x509.Certificate{ - SerialNumber: serialNumber, - Issuer: pkix.Name{ - CommonName: host, - }, - Subject: pkix.Name{ - CommonName: host, - }, - NotBefore: notBefore, - NotAfter: notAfter, - - KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, - ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, - BasicConstraintsValid: true, - } - - derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &privateKey.PublicKey, privateKey) - if err != nil { - err = fmt.Errorf("Failed to Create Certificate: %s", err) - return "", err - } - - pfxBytes, err := pkcs12.Encode(derBytes, privateKey, c.tmpCertificatePassword) - if err != nil { - err = fmt.Errorf("Failed to encode certificate as PFX: %s", err) - return "", err - } - - keyVaultDescription := keyVaultCertificate{ - Data: base64.StdEncoding.EncodeToString(pfxBytes), - DataType: "pfx", - Password: c.tmpCertificatePassword, - } - - bytes, err := json.Marshal(keyVaultDescription) - if err != nil { - err = fmt.Errorf("Failed to marshal key vault description: %s", err) - return "", err - } - - return base64.StdEncoding.EncodeToString(bytes), nil -} - -func (c *Config) Prepare(raws ...interface{}) ([]string, error) { - c.ctx.Funcs = azcommon.TemplateFuncs - err := config.Decode(c, &config.DecodeOpts{ - PluginType: BuilderId, - Interpolate: true, - InterpolateContext: &c.ctx, - }, raws...) - - if err != nil { - return nil, err - } - - provideDefaultValues(c) - setRuntimeValues(c) - err = setUserNamePassword(c) - if err != nil { - return nil, err - } - - // copy singular blocks - c.AzureTag.CopyOn(&c.AzureTags) - - err = c.ClientConfig.SetDefaultValues() - if err != nil { - return nil, err - } - - err = setCustomData(c) - if err != nil { - return nil, err - } - - // NOTE: if the user did not specify a communicator, then default to both - // SSH and WinRM. This is for backwards compatibility because the code did - // not specifically force the user to set a communicator. - if c.Comm.Type == "" || strings.EqualFold(c.Comm.Type, "ssh") { - err = setSshValues(c) - if err != nil { - return nil, err - } - } - - if c.Comm.Type == "" || strings.EqualFold(c.Comm.Type, "winrm") { - err = setWinRMCertificate(c) - if err != nil { - return nil, err - } - } - - var errs *packersdk.MultiError - errs = packersdk.MultiErrorAppend(errs, c.Comm.Prepare(&c.ctx)...) - - assertRequiredParametersSet(c, errs) - assertTagProperties(c, errs) - if errs != nil && len(errs.Errors) > 0 { - return nil, errs - } - - return nil, nil -} - -func setSshValues(c *Config) error { - if c.Comm.SSHTimeout == 0 { - c.Comm.SSHTimeout = 20 * time.Minute - } - - if c.Comm.SSHPrivateKeyFile != "" { - privateKeyBytes, err := c.Comm.ReadSSHPrivateKeyFile() - if err != nil { - return err - } - signer, err := ssh.ParsePrivateKey(privateKeyBytes) - if err != nil { - return err - } - - publicKey := signer.PublicKey() - c.sshAuthorizedKey = fmt.Sprintf("%s %s packer Azure Deployment%s", - publicKey.Type(), - base64.StdEncoding.EncodeToString(publicKey.Marshal()), - time.Now().Format(time.RFC3339)) - c.Comm.SSHPrivateKey = privateKeyBytes - - } else { - sshKeyPair, err := NewOpenSshKeyPair() - if err != nil { - return err - } - - c.sshAuthorizedKey = sshKeyPair.AuthorizedKey() - c.Comm.SSHPrivateKey = sshKeyPair.PrivateKey() - } - - return nil -} - -func setWinRMCertificate(c *Config) error { - c.Comm.WinRMTransportDecorator = - func() winrm.Transporter { - return &winrm.ClientNTLM{} - } - - cert, err := c.createCertificate() - c.winrmCertificate = cert - - return err -} - -func setRuntimeValues(c *Config) { - var tempName = NewTempName(c.CustomResourcePrefix) - - c.tmpAdminPassword = tempName.AdminPassword - // store so that we can access this later during provisioning - packersdk.LogSecretFilter.Set(c.tmpAdminPassword) - - c.tmpCertificatePassword = tempName.CertificatePassword - if c.TempComputeName == "" { - c.tmpComputeName = tempName.ComputeName - } else { - c.tmpComputeName = c.TempComputeName - } - c.tmpDeploymentName = tempName.DeploymentName - // Only set tmpResourceGroupName if no name has been specified - if c.TempResourceGroupName == "" && c.BuildResourceGroupName == "" { - c.tmpResourceGroupName = tempName.ResourceGroupName - } else if c.TempResourceGroupName != "" && c.BuildResourceGroupName == "" { - c.tmpResourceGroupName = c.TempResourceGroupName - } - if c.TempNicName == "" { - c.tmpNicName = tempName.NicName - } else { - c.tmpNicName = c.TempNicName - } - c.tmpNicName = tempName.NicName - c.tmpPublicIPAddressName = tempName.PublicIPAddressName - if c.TempOSDiskName == "" { - c.tmpOSDiskName = tempName.OSDiskName - } else { - c.tmpOSDiskName = c.TempOSDiskName - } - c.tmpDataDiskName = tempName.DataDiskName - c.tmpSubnetName = tempName.SubnetName - c.tmpVirtualNetworkName = tempName.VirtualNetworkName - c.tmpNsgName = tempName.NsgName - c.tmpKeyVaultName = tempName.KeyVaultName -} - -func setUserNamePassword(c *Config) error { - // Set default credentials generated by the builder - c.UserName = DefaultUserName - c.Password = c.tmpAdminPassword - - // Set communicator specific credentials and update defaults if different. - // Communicator specific credentials need to be updated as the standard Packer - // SSHConfigFunc and WinRMConfigFunc use communicator specific credentials, unless overwritten. - - // SSH comm - if c.Comm.SSHUsername == "" { - c.Comm.SSHUsername = c.UserName - } - c.UserName = c.Comm.SSHUsername - - // if user has an explicit wish to use an SSH password, we'll set it - if c.Comm.SSHPassword != "" { - c.Password = c.Comm.SSHPassword - } - - if c.Comm.Type == "ssh" { - return nil - } - - // WinRM comm - if c.Comm.WinRMUser == "" { - c.Comm.WinRMUser = c.UserName - } - c.UserName = c.Comm.WinRMUser - - if c.Comm.WinRMPassword == "" { - // Configure password settings using Azure generated credentials - c.Comm.WinRMPassword = c.Password - } - - if !isValidPassword(c.Comm.WinRMPassword) { - return fmt.Errorf("The supplied \"winrm_password\" must be between 8-123 characters long and must satisfy at least 3 from the following: \n1) Contains an uppercase character \n2) Contains a lowercase character\n3) Contains a numeric digit\n4) Contains a special character\n5) Control characters are not allowed") - } - c.Password = c.Comm.WinRMPassword - - return nil -} - -func setCustomData(c *Config) error { - if c.CustomDataFile == "" { - return nil - } - - b, err := ioutil.ReadFile(c.CustomDataFile) - if err != nil { - return err - } - - c.customData = base64.StdEncoding.EncodeToString(b) - return nil -} - -func provideDefaultValues(c *Config) { - if c.VMSize == "" { - c.VMSize = DefaultVMSize - } - - if c.ManagedImageStorageAccountType == "" { - c.managedImageStorageAccountType = compute.StorageAccountTypesStandardLRS - } - - if c.DiskCachingType == "" { - c.diskCachingType = compute.CachingTypesReadWrite - } - - if c.ImagePublisher != "" && c.ImageVersion == "" { - c.ImageVersion = DefaultImageVersion - } - - if c.BuildKeyVaultSKU == "" { - c.BuildKeyVaultSKU = DefaultKeyVaultSKU - } - - c.ClientConfig.SetDefaultValues() -} - -func assertTagProperties(c *Config, errs *packersdk.MultiError) { - if len(c.AzureTags) > 15 { - errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("a max of 15 tags are supported, but %d were provided", len(c.AzureTags))) - } - - for k, v := range c.AzureTags { - if len(k) > 512 { - errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("the tag name %q exceeds (%d) the 512 character limit", k, len(k))) - } - if len(v) > 256 { - errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("the tag name %q exceeds (%d) the 256 character limit", v, len(v))) - } - } -} - -//nolint:ineffassign //this triggers a false positive because errs is passed by reference -func assertRequiredParametersSet(c *Config, errs *packersdk.MultiError) { - c.ClientConfig.Validate(errs) - - ///////////////////////////////////////////// - // Identity - if len(c.UserAssignedManagedIdentities) != 0 { - for _, rid := range c.UserAssignedManagedIdentities { - r, err := client.ParseResourceID(rid) - if err != nil { - err := fmt.Errorf("Error parsing resource ID from `user_assigned_managed_identities`; please make sure"+ - " that this value follows the full resource id format: "+ - "/subscriptions//resourcegroups//providers/Microsoft.ManagedIdentity/userAssignedIdentities/.\n"+ - " Original error: %s", err) - errs = packersdk.MultiErrorAppend(errs, err) - } else { - if !strings.EqualFold(r.Provider, "Microsoft.ManagedIdentity") { - errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("A valid user assigned managed identity resource id must have a correct resource provider")) - } - if !strings.EqualFold(r.ResourceType.String(), "userAssignedIdentities") { - errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("A valid user assigned managed identity resource id must have a correct resource type")) - } - } - } - } - - ///////////////////////////////////////////// - // Capture - if c.CaptureContainerName == "" && c.ManagedImageName == "" { - errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("A capture_container_name or managed_image_name must be specified")) - } - - if c.CaptureNamePrefix == "" && c.ManagedImageResourceGroupName == "" { - errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("A capture_name_prefix or managed_image_resource_group_name must be specified")) - } - - if (c.CaptureNamePrefix != "" || c.CaptureContainerName != "") && (c.ManagedImageResourceGroupName != "" || c.ManagedImageName != "") { - errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("Either a VHD or a managed image can be built, but not both. Please specify either capture_container_name and capture_name_prefix or managed_image_resource_group_name and managed_image_name.")) - } - - if c.CaptureContainerName != "" { - if !reCaptureContainerName.MatchString(c.CaptureContainerName) { - errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("A capture_container_name must satisfy the regular expression %q.", reCaptureContainerName.String())) - } - - if strings.HasSuffix(c.CaptureContainerName, "-") { - errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("A capture_container_name must not end with a hyphen, e.g. '-'.")) - } - - if strings.Contains(c.CaptureContainerName, "--") { - errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("A capture_container_name must not contain consecutive hyphens, e.g. '--'.")) - } - - if c.CaptureNamePrefix == "" { - errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("A capture_name_prefix must be specified")) - } - - if !reCaptureNamePrefix.MatchString(c.CaptureNamePrefix) { - errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("A capture_name_prefix must satisfy the regular expression %q.", reCaptureNamePrefix.String())) - } - - if strings.HasSuffix(c.CaptureNamePrefix, "-") || strings.HasSuffix(c.CaptureNamePrefix, ".") { - errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("A capture_name_prefix must not end with a hyphen or period.")) - } - } - - if c.TempResourceGroupName != "" && c.BuildResourceGroupName != "" { - errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("The settings temp_resource_group_name and build_resource_group_name cannot both be defined. Please define one or neither.")) - } - - ///////////////////////////////////////////// - // Compute - toInt := func(b bool) int { - if b { - return 1 - } else { - return 0 - } - } - - isImageUrl := c.ImageUrl != "" - isCustomManagedImage := c.CustomManagedImageName != "" || c.CustomManagedImageResourceGroupName != "" - isSharedGallery := c.SharedGallery.GalleryName != "" - isPlatformImage := c.ImagePublisher != "" || c.ImageOffer != "" || c.ImageSku != "" - - countSourceInputs := toInt(isImageUrl) + toInt(isCustomManagedImage) + toInt(isPlatformImage) + toInt(isSharedGallery) - - if countSourceInputs > 1 { - errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("Specify either a VHD (image_url), Image Reference (image_publisher, image_offer, image_sku), a Managed Disk (custom_managed_disk_image_name, custom_managed_disk_resource_group_name), or a Shared Gallery Image (shared_image_gallery)")) - } - - if isImageUrl && c.ManagedImageResourceGroupName != "" { - errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("A managed image must be created from a managed image, it cannot be created from a VHD.")) - } - - if c.SharedGallery.GalleryName != "" { - if c.SharedGallery.Subscription == "" { - errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("A shared_image_gallery.subscription must be specified")) - } - if c.SharedGallery.ResourceGroup == "" { - errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("A shared_image_gallery.resource_group must be specified")) - } - if c.SharedGallery.ImageName == "" { - errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("A shared_image_gallery.image_name must be specified")) - } - if c.CaptureContainerName != "" { - errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("VHD Target [capture_container_name] is not supported when using Shared Image Gallery as source. Use managed_image_resource_group_name instead.")) - } - if c.CaptureNamePrefix != "" { - errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("VHD Target [capture_name_prefix] is not supported when using Shared Image Gallery as source. Use managed_image_name instead.")) - } - } else if c.ImageUrl == "" && c.CustomManagedImageName == "" { - if c.ImagePublisher == "" { - errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("An image_publisher must be specified")) - } - if c.ImageOffer == "" { - errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("An image_offer must be specified")) - } - if c.ImageSku == "" { - errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("An image_sku must be specified")) - } - } else if c.ImageUrl == "" && c.ImagePublisher == "" { - if c.CustomManagedImageResourceGroupName == "" { - errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("A custom_managed_image_resource_group_name must be specified")) - } - if c.CustomManagedImageName == "" { - errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("A custom_managed_image_name must be specified")) - } - if c.ManagedImageResourceGroupName == "" { - errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("A managed_image_resource_group_name must be specified")) - } - if c.ManagedImageName == "" { - errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("A managed_image_name must be specified")) - } - } else { - if c.ImagePublisher != "" || c.ImageOffer != "" || c.ImageSku != "" || c.ImageVersion != "" { - errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("An image_url must not be specified if image_publisher, image_offer, image_sku, or image_version is specified")) - } - } - - ///////////////////////////////////////////// - // Deployment - xor := func(a, b bool) bool { - return (a || b) && !(a && b) - } - - if !xor((c.StorageAccount != "" || c.ResourceGroupName != ""), (c.ManagedImageName != "" || c.ManagedImageResourceGroupName != "")) { - errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("Specify either a VHD (storage_account and resource_group_name) or Managed Image (managed_image_resource_group_name and managed_image_name) output")) - } - - if !xor(c.Location != "", c.BuildResourceGroupName != "") { - errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("Specify either a location to create the resource group in or an existing build_resource_group_name, but not both.")) - } - - if c.ManagedImageName == "" && c.ManagedImageResourceGroupName == "" { - if c.StorageAccount == "" { - errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("A storage_account must be specified")) - } - if c.ResourceGroupName == "" { - errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("A resource_group_name must be specified")) - } - } - - if c.TempResourceGroupName != "" { - if ok, err := assertResourceGroupName(c.TempResourceGroupName, "temp_resource_group_name"); !ok { - errs = packersdk.MultiErrorAppend(errs, err) - } - } - - if c.BuildResourceGroupName != "" { - if ok, err := assertResourceGroupName(c.BuildResourceGroupName, "build_resource_group_name"); !ok { - errs = packersdk.MultiErrorAppend(errs, err) - } - } - - if c.ManagedImageResourceGroupName != "" { - if ok, err := assertResourceGroupName(c.ManagedImageResourceGroupName, "managed_image_resource_group_name"); !ok { - errs = packersdk.MultiErrorAppend(errs, err) - } - } - - if c.ManagedImageName != "" { - if ok, err := assertManagedImageName(c.ManagedImageName, "managed_image_name"); !ok { - errs = packersdk.MultiErrorAppend(errs, err) - } - } - - if c.ManagedImageName != "" && c.ManagedImageResourceGroupName != "" && c.SharedGalleryDestination.SigDestinationGalleryName != "" { - if c.SharedGalleryDestination.SigDestinationResourceGroup == "" { - errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("A resource_group must be specified for shared_image_gallery_destination")) - } - if c.SharedGalleryDestination.SigDestinationImageName == "" { - errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("An image_name must be specified for shared_image_gallery_destination")) - } - if c.SharedGalleryDestination.SigDestinationImageVersion == "" { - errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("An image_version must be specified for shared_image_gallery_destination")) - } - if len(c.SharedGalleryDestination.SigDestinationReplicationRegions) == 0 { - errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("A list of replication_regions must be specified for shared_image_gallery_destination")) - } - if c.SharedGalleryDestination.SigDestinationSubscription == "" { - c.SharedGalleryDestination.SigDestinationSubscription = c.ClientConfig.SubscriptionID - } - } - if c.SharedGalleryTimeout == 0 { - // default to a one-hour timeout. In the sdk, the default is 15 m. - c.SharedGalleryTimeout = 60 * time.Minute - } - - if c.ManagedImageOSDiskSnapshotName != "" { - if ok, err := assertManagedImageOSDiskSnapshotName(c.ManagedImageOSDiskSnapshotName, "managed_image_os_disk_snapshot_name"); !ok { - errs = packersdk.MultiErrorAppend(errs, err) - } - } - - if c.ManagedImageDataDiskSnapshotPrefix != "" { - if ok, err := assertManagedImageDataDiskSnapshotName(c.ManagedImageDataDiskSnapshotPrefix, "managed_image_data_disk_snapshot_prefix"); !ok { - errs = packersdk.MultiErrorAppend(errs, err) - } - } - - if c.CustomResourcePrefix != "" { - if ok, err := assertResourceNamePrefix(c.CustomResourcePrefix, "custom_resource_build_prefix"); !ok { - errs = packersdk.MultiErrorAppend(errs, err) - } - } - - if c.VirtualNetworkName == "" && c.VirtualNetworkResourceGroupName != "" { - errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("If virtual_network_resource_group_name is specified, so must virtual_network_name")) - } - if c.VirtualNetworkName == "" && c.VirtualNetworkSubnetName != "" { - errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("If virtual_network_subnet_name is specified, so must virtual_network_name")) - } - - if c.AllowedInboundIpAddresses != nil && len(c.AllowedInboundIpAddresses) >= 1 { - if c.VirtualNetworkName != "" { - errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("If virtual_network_name is specified, allowed_inbound_ip_addresses cannot be specified")) - } else { - if ok, err := assertAllowedInboundIpAddresses(c.AllowedInboundIpAddresses, "allowed_inbound_ip_addresses"); !ok { - errs = packersdk.MultiErrorAppend(errs, err) - } - } - } - - ///////////////////////////////////////////// - // Plan Info - if c.PlanInfo.PlanName != "" || c.PlanInfo.PlanProduct != "" || c.PlanInfo.PlanPublisher != "" || c.PlanInfo.PlanPromotionCode != "" { - if c.PlanInfo.PlanName == "" || c.PlanInfo.PlanProduct == "" || c.PlanInfo.PlanPublisher == "" { - errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("if either plan_name, plan_product, plan_publisher, or plan_promotion_code are defined then plan_name, plan_product, and plan_publisher must be defined")) - } else { - if c.AzureTags == nil { - c.AzureTags = make(map[string]string) - } - - c.AzureTags["PlanInfo"] = c.PlanInfo.PlanName - c.AzureTags["PlanProduct"] = c.PlanInfo.PlanProduct - c.AzureTags["PlanPublisher"] = c.PlanInfo.PlanPublisher - c.AzureTags["PlanPromotionCode"] = c.PlanInfo.PlanPromotionCode - } - } - - ///////////////////////////////////////////// - // Polling Duration Timeout - if c.PollingDurationTimeout == 0 { - // In the sdk, the default is 15 m. - c.PollingDurationTimeout = 15 * time.Minute - } - - ///////////////////////////////////////////// - // OS - if strings.EqualFold(c.OSType, constants.Target_Linux) { - c.OSType = constants.Target_Linux - } else if strings.EqualFold(c.OSType, constants.Target_Windows) { - c.OSType = constants.Target_Windows - } else if c.OSType == "" { - errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("An os_type must be specified")) - } else { - errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("The os_type %q is invalid", c.OSType)) - } - - ///////////////////////////////////////////// - // Storage - switch c.ManagedImageStorageAccountType { - case "", string(compute.StorageAccountTypesStandardLRS): - c.managedImageStorageAccountType = compute.StorageAccountTypesStandardLRS - case string(compute.StorageAccountTypesPremiumLRS): - c.managedImageStorageAccountType = compute.StorageAccountTypesPremiumLRS - default: - errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("The managed_image_storage_account_type %q is invalid", c.ManagedImageStorageAccountType)) - } - - if ok, err := assertSigAllowedStorageAccountType(c.SharedGalleryDestination.SigDestinationStorageAccountType); !ok { - errs = packersdk.MultiErrorAppend(errs, err) - } - - switch c.DiskCachingType { - case string(compute.CachingTypesNone): - c.diskCachingType = compute.CachingTypesNone - case string(compute.CachingTypesReadOnly): - c.diskCachingType = compute.CachingTypesReadOnly - case "", string(compute.CachingTypesReadWrite): - c.diskCachingType = compute.CachingTypesReadWrite - default: - errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("The disk_caching_type %q is invalid", c.DiskCachingType)) - } -} - -func assertManagedImageName(name, setting string) (bool, error) { - if !isValidAzureName(reManagedDiskName, name) { - return false, fmt.Errorf("The setting %s must match the regular expression %q, and not end with a '-' or '.'.", setting, validManagedDiskName) - } - return true, nil -} - -func assertManagedImageOSDiskSnapshotName(name, setting string) (bool, error) { - if !isValidAzureName(reSnapshotName, name) { - return false, fmt.Errorf("The setting %s must only contain characters from a-z, A-Z, 0-9 and _ and the maximum length is 80 characters", setting) - } - return true, nil -} - -func assertManagedImageDataDiskSnapshotName(name, setting string) (bool, error) { - if !isValidAzureName(reSnapshotPrefix, name) { - return false, fmt.Errorf("The setting %s must only contain characters from a-z, A-Z, 0-9 and _ and the maximum length (excluding the prefix) is 60 characters", setting) - } - return true, nil -} - -func assertResourceNamePrefix(name, setting string) (bool, error) { - if !isValidAzureName(reResourceNamePrefix, name) { - return false, fmt.Errorf("The setting %s must only contain characters from a-z, A-Z, 0-9 and _ and the maximum length is 10 characters", setting) - } - return true, nil -} - -func assertAllowedInboundIpAddresses(ipAddresses []string, setting string) (bool, error) { - for _, ipAddress := range ipAddresses { - if net.ParseIP(ipAddress) == nil { - if _, _, err := net.ParseCIDR(ipAddress); err != nil { - return false, fmt.Errorf("The setting %s must only contain valid IP addresses or CIDR blocks", setting) - } - } - } - return true, nil -} - -func assertSigAllowedStorageAccountType(s string) (bool, error) { - _, err := getSigDestinationStorageAccountType(s) - if err != nil { - return false, err - } - return true, nil -} - -func assertResourceGroupName(rgn, setting string) (bool, error) { - if !isValidAzureName(reResourceGroupName, rgn) { - return false, fmt.Errorf("The setting %s must match the regular expression %q, and not end with a '-' or '.'.", setting, validResourceGroupNameRe) - } - return true, nil -} - -func isValidAzureName(re *regexp.Regexp, rgn string) bool { - return re.Match([]byte(rgn)) && - !strings.HasSuffix(rgn, ".") && - !strings.HasSuffix(rgn, "-") -} - -// The supplied password must be between 8-123 characters long and must satisfy at least 3 of password complexity requirements from the following: -// 1) Contains an uppercase character -// 2) Contains a lowercase character -// 3) Contains a numeric digit -// 4) Contains a special character -// 5) Control characters are not allowed (a very specific case - not included in this validation) -func isValidPassword(password string) bool { - if !(len(password) >= 8 && len(password) <= 123) { - return false - } - - requirements := 0 - if strings.ContainsAny(password, random.PossibleNumbers) { - requirements++ - } - if strings.ContainsAny(password, random.PossibleLowerCase) { - requirements++ - } - if strings.ContainsAny(password, random.PossibleUpperCase) { - requirements++ - } - if strings.ContainsAny(password, random.PossibleSpecialCharacter) { - requirements++ - } - - return requirements >= 3 -} - -func (c *Config) validateLocationZoneResiliency(say func(s string)) { - // Docs on regions that support Availibility Zones: - // https://docs.microsoft.com/en-us/azure/availability-zones/az-overview#regions-that-support-availability-zones - // Query technical names for locations: - // az account list-locations --query '[].name' -o tsv - - var zones = make(map[string]struct{}) - zones["westeurope"] = struct{}{} - zones["centralus"] = struct{}{} - zones["eastus2"] = struct{}{} - zones["francecentral"] = struct{}{} - zones["northeurope"] = struct{}{} - zones["southeastasia"] = struct{}{} - zones["westus2"] = struct{}{} - - if _, ok := zones[c.Location]; !ok { - say(fmt.Sprintf("WARNING: Zone resiliency may not be supported in %s, checkout the docs at https://docs.microsoft.com/en-us/azure/availability-zones/", c.Location)) - } -} diff --git a/builder/azure/arm/config.hcl2spec.go b/builder/azure/arm/config.hcl2spec.go deleted file mode 100644 index e520d2b9f..000000000 --- a/builder/azure/arm/config.hcl2spec.go +++ /dev/null @@ -1,361 +0,0 @@ -// Code generated by "packer-sdc mapstructure-to-hcl2"; DO NOT EDIT. - -package arm - -import ( - "github.com/hashicorp/hcl/v2/hcldec" - "github.com/hashicorp/packer-plugin-sdk/template/config" - "github.com/zclconf/go-cty/cty" -) - -// FlatConfig is an auto-generated flat version of Config. -// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. -type FlatConfig struct { - PackerBuildName *string `mapstructure:"packer_build_name" cty:"packer_build_name" hcl:"packer_build_name"` - PackerBuilderType *string `mapstructure:"packer_builder_type" cty:"packer_builder_type" hcl:"packer_builder_type"` - PackerCoreVersion *string `mapstructure:"packer_core_version" cty:"packer_core_version" hcl:"packer_core_version"` - PackerDebug *bool `mapstructure:"packer_debug" cty:"packer_debug" hcl:"packer_debug"` - PackerForce *bool `mapstructure:"packer_force" cty:"packer_force" hcl:"packer_force"` - PackerOnError *string `mapstructure:"packer_on_error" cty:"packer_on_error" hcl:"packer_on_error"` - PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables" hcl:"packer_user_variables"` - PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables" hcl:"packer_sensitive_variables"` - CloudEnvironmentName *string `mapstructure:"cloud_environment_name" required:"false" cty:"cloud_environment_name" hcl:"cloud_environment_name"` - ClientID *string `mapstructure:"client_id" cty:"client_id" hcl:"client_id"` - ClientSecret *string `mapstructure:"client_secret" cty:"client_secret" hcl:"client_secret"` - ClientCertPath *string `mapstructure:"client_cert_path" cty:"client_cert_path" hcl:"client_cert_path"` - ClientCertExpireTimeout *string `mapstructure:"client_cert_token_timeout" required:"false" cty:"client_cert_token_timeout" hcl:"client_cert_token_timeout"` - ClientJWT *string `mapstructure:"client_jwt" cty:"client_jwt" hcl:"client_jwt"` - ObjectID *string `mapstructure:"object_id" cty:"object_id" hcl:"object_id"` - TenantID *string `mapstructure:"tenant_id" required:"false" cty:"tenant_id" hcl:"tenant_id"` - SubscriptionID *string `mapstructure:"subscription_id" cty:"subscription_id" hcl:"subscription_id"` - UseAzureCLIAuth *bool `mapstructure:"use_azure_cli_auth" required:"false" cty:"use_azure_cli_auth" hcl:"use_azure_cli_auth"` - UserAssignedManagedIdentities []string `mapstructure:"user_assigned_managed_identities" required:"false" cty:"user_assigned_managed_identities" hcl:"user_assigned_managed_identities"` - CaptureNamePrefix *string `mapstructure:"capture_name_prefix" cty:"capture_name_prefix" hcl:"capture_name_prefix"` - CaptureContainerName *string `mapstructure:"capture_container_name" cty:"capture_container_name" hcl:"capture_container_name"` - SharedGallery *FlatSharedImageGallery `mapstructure:"shared_image_gallery" required:"false" cty:"shared_image_gallery" hcl:"shared_image_gallery"` - SharedGalleryDestination *FlatSharedImageGalleryDestination `mapstructure:"shared_image_gallery_destination" cty:"shared_image_gallery_destination" hcl:"shared_image_gallery_destination"` - SharedGalleryTimeout *string `mapstructure:"shared_image_gallery_timeout" cty:"shared_image_gallery_timeout" hcl:"shared_image_gallery_timeout"` - SharedGalleryImageVersionEndOfLifeDate *string `mapstructure:"shared_gallery_image_version_end_of_life_date" required:"false" cty:"shared_gallery_image_version_end_of_life_date" hcl:"shared_gallery_image_version_end_of_life_date"` - SharedGalleryImageVersionReplicaCount *int32 `mapstructure:"shared_image_gallery_replica_count" required:"false" cty:"shared_image_gallery_replica_count" hcl:"shared_image_gallery_replica_count"` - SharedGalleryImageVersionExcludeFromLatest *bool `mapstructure:"shared_gallery_image_version_exclude_from_latest" required:"false" cty:"shared_gallery_image_version_exclude_from_latest" hcl:"shared_gallery_image_version_exclude_from_latest"` - ImagePublisher *string `mapstructure:"image_publisher" required:"true" cty:"image_publisher" hcl:"image_publisher"` - ImageOffer *string `mapstructure:"image_offer" required:"true" cty:"image_offer" hcl:"image_offer"` - ImageSku *string `mapstructure:"image_sku" required:"true" cty:"image_sku" hcl:"image_sku"` - ImageVersion *string `mapstructure:"image_version" required:"false" cty:"image_version" hcl:"image_version"` - ImageUrl *string `mapstructure:"image_url" required:"true" cty:"image_url" hcl:"image_url"` - CustomManagedImageName *string `mapstructure:"custom_managed_image_name" required:"true" cty:"custom_managed_image_name" hcl:"custom_managed_image_name"` - CustomManagedImageResourceGroupName *string `mapstructure:"custom_managed_image_resource_group_name" required:"true" cty:"custom_managed_image_resource_group_name" hcl:"custom_managed_image_resource_group_name"` - Location *string `mapstructure:"location" cty:"location" hcl:"location"` - VMSize *string `mapstructure:"vm_size" required:"false" cty:"vm_size" hcl:"vm_size"` - ManagedImageResourceGroupName *string `mapstructure:"managed_image_resource_group_name" cty:"managed_image_resource_group_name" hcl:"managed_image_resource_group_name"` - ManagedImageName *string `mapstructure:"managed_image_name" cty:"managed_image_name" hcl:"managed_image_name"` - ManagedImageStorageAccountType *string `mapstructure:"managed_image_storage_account_type" required:"false" cty:"managed_image_storage_account_type" hcl:"managed_image_storage_account_type"` - ManagedImageOSDiskSnapshotName *string `mapstructure:"managed_image_os_disk_snapshot_name" required:"false" cty:"managed_image_os_disk_snapshot_name" hcl:"managed_image_os_disk_snapshot_name"` - ManagedImageDataDiskSnapshotPrefix *string `mapstructure:"managed_image_data_disk_snapshot_prefix" required:"false" cty:"managed_image_data_disk_snapshot_prefix" hcl:"managed_image_data_disk_snapshot_prefix"` - KeepOSDisk *bool `mapstructure:"keep_os_disk" required:"false" cty:"keep_os_disk" hcl:"keep_os_disk"` - ManagedImageZoneResilient *bool `mapstructure:"managed_image_zone_resilient" required:"false" cty:"managed_image_zone_resilient" hcl:"managed_image_zone_resilient"` - AzureTags map[string]string `mapstructure:"azure_tags" required:"false" cty:"azure_tags" hcl:"azure_tags"` - AzureTag []config.FlatNameValue `mapstructure:"azure_tag" required:"false" cty:"azure_tag" hcl:"azure_tag"` - ResourceGroupName *string `mapstructure:"resource_group_name" cty:"resource_group_name" hcl:"resource_group_name"` - StorageAccount *string `mapstructure:"storage_account" cty:"storage_account" hcl:"storage_account"` - TempComputeName *string `mapstructure:"temp_compute_name" required:"false" cty:"temp_compute_name" hcl:"temp_compute_name"` - TempNicName *string `mapstructure:"temp_nic_name" required:"false" cty:"temp_nic_name" hcl:"temp_nic_name"` - TempResourceGroupName *string `mapstructure:"temp_resource_group_name" cty:"temp_resource_group_name" hcl:"temp_resource_group_name"` - BuildResourceGroupName *string `mapstructure:"build_resource_group_name" cty:"build_resource_group_name" hcl:"build_resource_group_name"` - BuildKeyVaultName *string `mapstructure:"build_key_vault_name" cty:"build_key_vault_name" hcl:"build_key_vault_name"` - BuildKeyVaultSKU *string `mapstructure:"build_key_vault_sku" cty:"build_key_vault_sku" hcl:"build_key_vault_sku"` - PrivateVirtualNetworkWithPublicIp *bool `mapstructure:"private_virtual_network_with_public_ip" required:"false" cty:"private_virtual_network_with_public_ip" hcl:"private_virtual_network_with_public_ip"` - VirtualNetworkName *string `mapstructure:"virtual_network_name" required:"false" cty:"virtual_network_name" hcl:"virtual_network_name"` - VirtualNetworkSubnetName *string `mapstructure:"virtual_network_subnet_name" required:"false" cty:"virtual_network_subnet_name" hcl:"virtual_network_subnet_name"` - VirtualNetworkResourceGroupName *string `mapstructure:"virtual_network_resource_group_name" required:"false" cty:"virtual_network_resource_group_name" hcl:"virtual_network_resource_group_name"` - CustomDataFile *string `mapstructure:"custom_data_file" required:"false" cty:"custom_data_file" hcl:"custom_data_file"` - PlanInfo *FlatPlanInformation `mapstructure:"plan_info" required:"false" cty:"plan_info" hcl:"plan_info"` - PollingDurationTimeout *string `mapstructure:"polling_duration_timeout" required:"false" cty:"polling_duration_timeout" hcl:"polling_duration_timeout"` - OSType *string `mapstructure:"os_type" required:"false" cty:"os_type" hcl:"os_type"` - TempOSDiskName *string `mapstructure:"temp_os_disk_name" required:"false" cty:"temp_os_disk_name" hcl:"temp_os_disk_name"` - OSDiskSizeGB *int32 `mapstructure:"os_disk_size_gb" required:"false" cty:"os_disk_size_gb" hcl:"os_disk_size_gb"` - AdditionalDiskSize []int32 `mapstructure:"disk_additional_size" required:"false" cty:"disk_additional_size" hcl:"disk_additional_size"` - DiskCachingType *string `mapstructure:"disk_caching_type" required:"false" cty:"disk_caching_type" hcl:"disk_caching_type"` - AllowedInboundIpAddresses []string `mapstructure:"allowed_inbound_ip_addresses" cty:"allowed_inbound_ip_addresses" hcl:"allowed_inbound_ip_addresses"` - BootDiagSTGAccount *string `mapstructure:"boot_diag_storage_account" required:"false" cty:"boot_diag_storage_account" hcl:"boot_diag_storage_account"` - CustomResourcePrefix *string `mapstructure:"custom_resource_build_prefix" required:"false" cty:"custom_resource_build_prefix" hcl:"custom_resource_build_prefix"` - Type *string `mapstructure:"communicator" cty:"communicator" hcl:"communicator"` - PauseBeforeConnect *string `mapstructure:"pause_before_connecting" cty:"pause_before_connecting" hcl:"pause_before_connecting"` - SSHHost *string `mapstructure:"ssh_host" cty:"ssh_host" hcl:"ssh_host"` - SSHPort *int `mapstructure:"ssh_port" cty:"ssh_port" hcl:"ssh_port"` - SSHUsername *string `mapstructure:"ssh_username" cty:"ssh_username" hcl:"ssh_username"` - SSHPassword *string `mapstructure:"ssh_password" cty:"ssh_password" hcl:"ssh_password"` - SSHKeyPairName *string `mapstructure:"ssh_keypair_name" undocumented:"true" cty:"ssh_keypair_name" hcl:"ssh_keypair_name"` - SSHTemporaryKeyPairName *string `mapstructure:"temporary_key_pair_name" undocumented:"true" cty:"temporary_key_pair_name" hcl:"temporary_key_pair_name"` - SSHTemporaryKeyPairType *string `mapstructure:"temporary_key_pair_type" cty:"temporary_key_pair_type" hcl:"temporary_key_pair_type"` - SSHTemporaryKeyPairBits *int `mapstructure:"temporary_key_pair_bits" cty:"temporary_key_pair_bits" hcl:"temporary_key_pair_bits"` - SSHCiphers []string `mapstructure:"ssh_ciphers" cty:"ssh_ciphers" hcl:"ssh_ciphers"` - SSHClearAuthorizedKeys *bool `mapstructure:"ssh_clear_authorized_keys" cty:"ssh_clear_authorized_keys" hcl:"ssh_clear_authorized_keys"` - SSHKEXAlgos []string `mapstructure:"ssh_key_exchange_algorithms" cty:"ssh_key_exchange_algorithms" hcl:"ssh_key_exchange_algorithms"` - SSHPrivateKeyFile *string `mapstructure:"ssh_private_key_file" undocumented:"true" cty:"ssh_private_key_file" hcl:"ssh_private_key_file"` - SSHCertificateFile *string `mapstructure:"ssh_certificate_file" cty:"ssh_certificate_file" hcl:"ssh_certificate_file"` - SSHPty *bool `mapstructure:"ssh_pty" cty:"ssh_pty" hcl:"ssh_pty"` - SSHTimeout *string `mapstructure:"ssh_timeout" cty:"ssh_timeout" hcl:"ssh_timeout"` - SSHWaitTimeout *string `mapstructure:"ssh_wait_timeout" undocumented:"true" cty:"ssh_wait_timeout" hcl:"ssh_wait_timeout"` - SSHAgentAuth *bool `mapstructure:"ssh_agent_auth" undocumented:"true" cty:"ssh_agent_auth" hcl:"ssh_agent_auth"` - SSHDisableAgentForwarding *bool `mapstructure:"ssh_disable_agent_forwarding" cty:"ssh_disable_agent_forwarding" hcl:"ssh_disable_agent_forwarding"` - SSHHandshakeAttempts *int `mapstructure:"ssh_handshake_attempts" cty:"ssh_handshake_attempts" hcl:"ssh_handshake_attempts"` - SSHBastionHost *string `mapstructure:"ssh_bastion_host" cty:"ssh_bastion_host" hcl:"ssh_bastion_host"` - SSHBastionPort *int `mapstructure:"ssh_bastion_port" cty:"ssh_bastion_port" hcl:"ssh_bastion_port"` - SSHBastionAgentAuth *bool `mapstructure:"ssh_bastion_agent_auth" cty:"ssh_bastion_agent_auth" hcl:"ssh_bastion_agent_auth"` - SSHBastionUsername *string `mapstructure:"ssh_bastion_username" cty:"ssh_bastion_username" hcl:"ssh_bastion_username"` - SSHBastionPassword *string `mapstructure:"ssh_bastion_password" cty:"ssh_bastion_password" hcl:"ssh_bastion_password"` - SSHBastionInteractive *bool `mapstructure:"ssh_bastion_interactive" cty:"ssh_bastion_interactive" hcl:"ssh_bastion_interactive"` - SSHBastionPrivateKeyFile *string `mapstructure:"ssh_bastion_private_key_file" cty:"ssh_bastion_private_key_file" hcl:"ssh_bastion_private_key_file"` - SSHBastionCertificateFile *string `mapstructure:"ssh_bastion_certificate_file" cty:"ssh_bastion_certificate_file" hcl:"ssh_bastion_certificate_file"` - SSHFileTransferMethod *string `mapstructure:"ssh_file_transfer_method" cty:"ssh_file_transfer_method" hcl:"ssh_file_transfer_method"` - SSHProxyHost *string `mapstructure:"ssh_proxy_host" cty:"ssh_proxy_host" hcl:"ssh_proxy_host"` - SSHProxyPort *int `mapstructure:"ssh_proxy_port" cty:"ssh_proxy_port" hcl:"ssh_proxy_port"` - SSHProxyUsername *string `mapstructure:"ssh_proxy_username" cty:"ssh_proxy_username" hcl:"ssh_proxy_username"` - SSHProxyPassword *string `mapstructure:"ssh_proxy_password" cty:"ssh_proxy_password" hcl:"ssh_proxy_password"` - SSHKeepAliveInterval *string `mapstructure:"ssh_keep_alive_interval" cty:"ssh_keep_alive_interval" hcl:"ssh_keep_alive_interval"` - SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout" hcl:"ssh_read_write_timeout"` - SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels" hcl:"ssh_remote_tunnels"` - SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels" hcl:"ssh_local_tunnels"` - SSHPublicKey []byte `mapstructure:"ssh_public_key" undocumented:"true" cty:"ssh_public_key" hcl:"ssh_public_key"` - SSHPrivateKey []byte `mapstructure:"ssh_private_key" undocumented:"true" cty:"ssh_private_key" hcl:"ssh_private_key"` - WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username" hcl:"winrm_username"` - WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password" hcl:"winrm_password"` - WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host" hcl:"winrm_host"` - WinRMNoProxy *bool `mapstructure:"winrm_no_proxy" cty:"winrm_no_proxy" hcl:"winrm_no_proxy"` - WinRMPort *int `mapstructure:"winrm_port" cty:"winrm_port" hcl:"winrm_port"` - WinRMTimeout *string `mapstructure:"winrm_timeout" cty:"winrm_timeout" hcl:"winrm_timeout"` - WinRMUseSSL *bool `mapstructure:"winrm_use_ssl" cty:"winrm_use_ssl" hcl:"winrm_use_ssl"` - WinRMInsecure *bool `mapstructure:"winrm_insecure" cty:"winrm_insecure" hcl:"winrm_insecure"` - WinRMUseNTLM *bool `mapstructure:"winrm_use_ntlm" cty:"winrm_use_ntlm" hcl:"winrm_use_ntlm"` - AsyncResourceGroupDelete *bool `mapstructure:"async_resourcegroup_delete" required:"false" cty:"async_resourcegroup_delete" hcl:"async_resourcegroup_delete"` -} - -// FlatMapstructure returns a new FlatConfig. -// FlatConfig is an auto-generated flat version of Config. -// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { - return new(FlatConfig) -} - -// HCL2Spec returns the hcl spec of a Config. -// This spec is used by HCL to read the fields of Config. -// The decoded values from this spec will then be applied to a FlatConfig. -func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { - s := map[string]hcldec.Spec{ - "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, - "packer_builder_type": &hcldec.AttrSpec{Name: "packer_builder_type", Type: cty.String, Required: false}, - "packer_core_version": &hcldec.AttrSpec{Name: "packer_core_version", Type: cty.String, Required: false}, - "packer_debug": &hcldec.AttrSpec{Name: "packer_debug", Type: cty.Bool, Required: false}, - "packer_force": &hcldec.AttrSpec{Name: "packer_force", Type: cty.Bool, Required: false}, - "packer_on_error": &hcldec.AttrSpec{Name: "packer_on_error", Type: cty.String, Required: false}, - "packer_user_variables": &hcldec.AttrSpec{Name: "packer_user_variables", Type: cty.Map(cty.String), Required: false}, - "packer_sensitive_variables": &hcldec.AttrSpec{Name: "packer_sensitive_variables", Type: cty.List(cty.String), Required: false}, - "cloud_environment_name": &hcldec.AttrSpec{Name: "cloud_environment_name", Type: cty.String, Required: false}, - "client_id": &hcldec.AttrSpec{Name: "client_id", Type: cty.String, Required: false}, - "client_secret": &hcldec.AttrSpec{Name: "client_secret", Type: cty.String, Required: false}, - "client_cert_path": &hcldec.AttrSpec{Name: "client_cert_path", Type: cty.String, Required: false}, - "client_cert_token_timeout": &hcldec.AttrSpec{Name: "client_cert_token_timeout", Type: cty.String, Required: false}, - "client_jwt": &hcldec.AttrSpec{Name: "client_jwt", Type: cty.String, Required: false}, - "object_id": &hcldec.AttrSpec{Name: "object_id", Type: cty.String, Required: false}, - "tenant_id": &hcldec.AttrSpec{Name: "tenant_id", Type: cty.String, Required: false}, - "subscription_id": &hcldec.AttrSpec{Name: "subscription_id", Type: cty.String, Required: false}, - "use_azure_cli_auth": &hcldec.AttrSpec{Name: "use_azure_cli_auth", Type: cty.Bool, Required: false}, - "user_assigned_managed_identities": &hcldec.AttrSpec{Name: "user_assigned_managed_identities", Type: cty.List(cty.String), Required: false}, - "capture_name_prefix": &hcldec.AttrSpec{Name: "capture_name_prefix", Type: cty.String, Required: false}, - "capture_container_name": &hcldec.AttrSpec{Name: "capture_container_name", Type: cty.String, Required: false}, - "shared_image_gallery": &hcldec.BlockSpec{TypeName: "shared_image_gallery", Nested: hcldec.ObjectSpec((*FlatSharedImageGallery)(nil).HCL2Spec())}, - "shared_image_gallery_destination": &hcldec.BlockSpec{TypeName: "shared_image_gallery_destination", Nested: hcldec.ObjectSpec((*FlatSharedImageGalleryDestination)(nil).HCL2Spec())}, - "shared_image_gallery_timeout": &hcldec.AttrSpec{Name: "shared_image_gallery_timeout", Type: cty.String, Required: false}, - "shared_gallery_image_version_end_of_life_date": &hcldec.AttrSpec{Name: "shared_gallery_image_version_end_of_life_date", Type: cty.String, Required: false}, - "shared_image_gallery_replica_count": &hcldec.AttrSpec{Name: "shared_image_gallery_replica_count", Type: cty.Number, Required: false}, - "shared_gallery_image_version_exclude_from_latest": &hcldec.AttrSpec{Name: "shared_gallery_image_version_exclude_from_latest", Type: cty.Bool, Required: false}, - "image_publisher": &hcldec.AttrSpec{Name: "image_publisher", Type: cty.String, Required: false}, - "image_offer": &hcldec.AttrSpec{Name: "image_offer", Type: cty.String, Required: false}, - "image_sku": &hcldec.AttrSpec{Name: "image_sku", Type: cty.String, Required: false}, - "image_version": &hcldec.AttrSpec{Name: "image_version", Type: cty.String, Required: false}, - "image_url": &hcldec.AttrSpec{Name: "image_url", Type: cty.String, Required: false}, - "custom_managed_image_name": &hcldec.AttrSpec{Name: "custom_managed_image_name", Type: cty.String, Required: false}, - "custom_managed_image_resource_group_name": &hcldec.AttrSpec{Name: "custom_managed_image_resource_group_name", Type: cty.String, Required: false}, - "location": &hcldec.AttrSpec{Name: "location", Type: cty.String, Required: false}, - "vm_size": &hcldec.AttrSpec{Name: "vm_size", Type: cty.String, Required: false}, - "managed_image_resource_group_name": &hcldec.AttrSpec{Name: "managed_image_resource_group_name", Type: cty.String, Required: false}, - "managed_image_name": &hcldec.AttrSpec{Name: "managed_image_name", Type: cty.String, Required: false}, - "managed_image_storage_account_type": &hcldec.AttrSpec{Name: "managed_image_storage_account_type", Type: cty.String, Required: false}, - "managed_image_os_disk_snapshot_name": &hcldec.AttrSpec{Name: "managed_image_os_disk_snapshot_name", Type: cty.String, Required: false}, - "managed_image_data_disk_snapshot_prefix": &hcldec.AttrSpec{Name: "managed_image_data_disk_snapshot_prefix", Type: cty.String, Required: false}, - "keep_os_disk": &hcldec.AttrSpec{Name: "keep_os_disk", Type: cty.Bool, Required: false}, - "managed_image_zone_resilient": &hcldec.AttrSpec{Name: "managed_image_zone_resilient", Type: cty.Bool, Required: false}, - "azure_tags": &hcldec.AttrSpec{Name: "azure_tags", Type: cty.Map(cty.String), Required: false}, - "azure_tag": &hcldec.BlockListSpec{TypeName: "azure_tag", Nested: hcldec.ObjectSpec((*config.FlatNameValue)(nil).HCL2Spec())}, - "resource_group_name": &hcldec.AttrSpec{Name: "resource_group_name", Type: cty.String, Required: false}, - "storage_account": &hcldec.AttrSpec{Name: "storage_account", Type: cty.String, Required: false}, - "temp_compute_name": &hcldec.AttrSpec{Name: "temp_compute_name", Type: cty.String, Required: false}, - "temp_nic_name": &hcldec.AttrSpec{Name: "temp_nic_name", Type: cty.String, Required: false}, - "temp_resource_group_name": &hcldec.AttrSpec{Name: "temp_resource_group_name", Type: cty.String, Required: false}, - "build_resource_group_name": &hcldec.AttrSpec{Name: "build_resource_group_name", Type: cty.String, Required: false}, - "build_key_vault_name": &hcldec.AttrSpec{Name: "build_key_vault_name", Type: cty.String, Required: false}, - "build_key_vault_sku": &hcldec.AttrSpec{Name: "build_key_vault_sku", Type: cty.String, Required: false}, - "private_virtual_network_with_public_ip": &hcldec.AttrSpec{Name: "private_virtual_network_with_public_ip", Type: cty.Bool, Required: false}, - "virtual_network_name": &hcldec.AttrSpec{Name: "virtual_network_name", Type: cty.String, Required: false}, - "virtual_network_subnet_name": &hcldec.AttrSpec{Name: "virtual_network_subnet_name", Type: cty.String, Required: false}, - "virtual_network_resource_group_name": &hcldec.AttrSpec{Name: "virtual_network_resource_group_name", Type: cty.String, Required: false}, - "custom_data_file": &hcldec.AttrSpec{Name: "custom_data_file", Type: cty.String, Required: false}, - "plan_info": &hcldec.BlockSpec{TypeName: "plan_info", Nested: hcldec.ObjectSpec((*FlatPlanInformation)(nil).HCL2Spec())}, - "polling_duration_timeout": &hcldec.AttrSpec{Name: "polling_duration_timeout", Type: cty.String, Required: false}, - "os_type": &hcldec.AttrSpec{Name: "os_type", Type: cty.String, Required: false}, - "temp_os_disk_name": &hcldec.AttrSpec{Name: "temp_os_disk_name", Type: cty.String, Required: false}, - "os_disk_size_gb": &hcldec.AttrSpec{Name: "os_disk_size_gb", Type: cty.Number, Required: false}, - "disk_additional_size": &hcldec.AttrSpec{Name: "disk_additional_size", Type: cty.List(cty.Number), Required: false}, - "disk_caching_type": &hcldec.AttrSpec{Name: "disk_caching_type", Type: cty.String, Required: false}, - "allowed_inbound_ip_addresses": &hcldec.AttrSpec{Name: "allowed_inbound_ip_addresses", Type: cty.List(cty.String), Required: false}, - "boot_diag_storage_account": &hcldec.AttrSpec{Name: "boot_diag_storage_account", Type: cty.String, Required: false}, - "custom_resource_build_prefix": &hcldec.AttrSpec{Name: "custom_resource_build_prefix", Type: cty.String, Required: false}, - "communicator": &hcldec.AttrSpec{Name: "communicator", Type: cty.String, Required: false}, - "pause_before_connecting": &hcldec.AttrSpec{Name: "pause_before_connecting", Type: cty.String, Required: false}, - "ssh_host": &hcldec.AttrSpec{Name: "ssh_host", Type: cty.String, Required: false}, - "ssh_port": &hcldec.AttrSpec{Name: "ssh_port", Type: cty.Number, Required: false}, - "ssh_username": &hcldec.AttrSpec{Name: "ssh_username", Type: cty.String, Required: false}, - "ssh_password": &hcldec.AttrSpec{Name: "ssh_password", Type: cty.String, Required: false}, - "ssh_keypair_name": &hcldec.AttrSpec{Name: "ssh_keypair_name", Type: cty.String, Required: false}, - "temporary_key_pair_name": &hcldec.AttrSpec{Name: "temporary_key_pair_name", Type: cty.String, Required: false}, - "temporary_key_pair_type": &hcldec.AttrSpec{Name: "temporary_key_pair_type", Type: cty.String, Required: false}, - "temporary_key_pair_bits": &hcldec.AttrSpec{Name: "temporary_key_pair_bits", Type: cty.Number, Required: false}, - "ssh_ciphers": &hcldec.AttrSpec{Name: "ssh_ciphers", Type: cty.List(cty.String), Required: false}, - "ssh_clear_authorized_keys": &hcldec.AttrSpec{Name: "ssh_clear_authorized_keys", Type: cty.Bool, Required: false}, - "ssh_key_exchange_algorithms": &hcldec.AttrSpec{Name: "ssh_key_exchange_algorithms", Type: cty.List(cty.String), Required: false}, - "ssh_private_key_file": &hcldec.AttrSpec{Name: "ssh_private_key_file", Type: cty.String, Required: false}, - "ssh_certificate_file": &hcldec.AttrSpec{Name: "ssh_certificate_file", Type: cty.String, Required: false}, - "ssh_pty": &hcldec.AttrSpec{Name: "ssh_pty", Type: cty.Bool, Required: false}, - "ssh_timeout": &hcldec.AttrSpec{Name: "ssh_timeout", Type: cty.String, Required: false}, - "ssh_wait_timeout": &hcldec.AttrSpec{Name: "ssh_wait_timeout", Type: cty.String, Required: false}, - "ssh_agent_auth": &hcldec.AttrSpec{Name: "ssh_agent_auth", Type: cty.Bool, Required: false}, - "ssh_disable_agent_forwarding": &hcldec.AttrSpec{Name: "ssh_disable_agent_forwarding", Type: cty.Bool, Required: false}, - "ssh_handshake_attempts": &hcldec.AttrSpec{Name: "ssh_handshake_attempts", Type: cty.Number, Required: false}, - "ssh_bastion_host": &hcldec.AttrSpec{Name: "ssh_bastion_host", Type: cty.String, Required: false}, - "ssh_bastion_port": &hcldec.AttrSpec{Name: "ssh_bastion_port", Type: cty.Number, Required: false}, - "ssh_bastion_agent_auth": &hcldec.AttrSpec{Name: "ssh_bastion_agent_auth", Type: cty.Bool, Required: false}, - "ssh_bastion_username": &hcldec.AttrSpec{Name: "ssh_bastion_username", Type: cty.String, Required: false}, - "ssh_bastion_password": &hcldec.AttrSpec{Name: "ssh_bastion_password", Type: cty.String, Required: false}, - "ssh_bastion_interactive": &hcldec.AttrSpec{Name: "ssh_bastion_interactive", Type: cty.Bool, Required: false}, - "ssh_bastion_private_key_file": &hcldec.AttrSpec{Name: "ssh_bastion_private_key_file", Type: cty.String, Required: false}, - "ssh_bastion_certificate_file": &hcldec.AttrSpec{Name: "ssh_bastion_certificate_file", Type: cty.String, Required: false}, - "ssh_file_transfer_method": &hcldec.AttrSpec{Name: "ssh_file_transfer_method", Type: cty.String, Required: false}, - "ssh_proxy_host": &hcldec.AttrSpec{Name: "ssh_proxy_host", Type: cty.String, Required: false}, - "ssh_proxy_port": &hcldec.AttrSpec{Name: "ssh_proxy_port", Type: cty.Number, Required: false}, - "ssh_proxy_username": &hcldec.AttrSpec{Name: "ssh_proxy_username", Type: cty.String, Required: false}, - "ssh_proxy_password": &hcldec.AttrSpec{Name: "ssh_proxy_password", Type: cty.String, Required: false}, - "ssh_keep_alive_interval": &hcldec.AttrSpec{Name: "ssh_keep_alive_interval", Type: cty.String, Required: false}, - "ssh_read_write_timeout": &hcldec.AttrSpec{Name: "ssh_read_write_timeout", Type: cty.String, Required: false}, - "ssh_remote_tunnels": &hcldec.AttrSpec{Name: "ssh_remote_tunnels", Type: cty.List(cty.String), Required: false}, - "ssh_local_tunnels": &hcldec.AttrSpec{Name: "ssh_local_tunnels", Type: cty.List(cty.String), Required: false}, - "ssh_public_key": &hcldec.AttrSpec{Name: "ssh_public_key", Type: cty.List(cty.Number), Required: false}, - "ssh_private_key": &hcldec.AttrSpec{Name: "ssh_private_key", Type: cty.List(cty.Number), Required: false}, - "winrm_username": &hcldec.AttrSpec{Name: "winrm_username", Type: cty.String, Required: false}, - "winrm_password": &hcldec.AttrSpec{Name: "winrm_password", Type: cty.String, Required: false}, - "winrm_host": &hcldec.AttrSpec{Name: "winrm_host", Type: cty.String, Required: false}, - "winrm_no_proxy": &hcldec.AttrSpec{Name: "winrm_no_proxy", Type: cty.Bool, Required: false}, - "winrm_port": &hcldec.AttrSpec{Name: "winrm_port", Type: cty.Number, Required: false}, - "winrm_timeout": &hcldec.AttrSpec{Name: "winrm_timeout", Type: cty.String, Required: false}, - "winrm_use_ssl": &hcldec.AttrSpec{Name: "winrm_use_ssl", Type: cty.Bool, Required: false}, - "winrm_insecure": &hcldec.AttrSpec{Name: "winrm_insecure", Type: cty.Bool, Required: false}, - "winrm_use_ntlm": &hcldec.AttrSpec{Name: "winrm_use_ntlm", Type: cty.Bool, Required: false}, - "async_resourcegroup_delete": &hcldec.AttrSpec{Name: "async_resourcegroup_delete", Type: cty.Bool, Required: false}, - } - return s -} - -// FlatPlanInformation is an auto-generated flat version of PlanInformation. -// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. -type FlatPlanInformation struct { - PlanName *string `mapstructure:"plan_name" cty:"plan_name" hcl:"plan_name"` - PlanProduct *string `mapstructure:"plan_product" cty:"plan_product" hcl:"plan_product"` - PlanPublisher *string `mapstructure:"plan_publisher" cty:"plan_publisher" hcl:"plan_publisher"` - PlanPromotionCode *string `mapstructure:"plan_promotion_code" cty:"plan_promotion_code" hcl:"plan_promotion_code"` -} - -// FlatMapstructure returns a new FlatPlanInformation. -// FlatPlanInformation is an auto-generated flat version of PlanInformation. -// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*PlanInformation) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { - return new(FlatPlanInformation) -} - -// HCL2Spec returns the hcl spec of a PlanInformation. -// This spec is used by HCL to read the fields of PlanInformation. -// The decoded values from this spec will then be applied to a FlatPlanInformation. -func (*FlatPlanInformation) HCL2Spec() map[string]hcldec.Spec { - s := map[string]hcldec.Spec{ - "plan_name": &hcldec.AttrSpec{Name: "plan_name", Type: cty.String, Required: false}, - "plan_product": &hcldec.AttrSpec{Name: "plan_product", Type: cty.String, Required: false}, - "plan_publisher": &hcldec.AttrSpec{Name: "plan_publisher", Type: cty.String, Required: false}, - "plan_promotion_code": &hcldec.AttrSpec{Name: "plan_promotion_code", Type: cty.String, Required: false}, - } - return s -} - -// FlatSharedImageGallery is an auto-generated flat version of SharedImageGallery. -// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. -type FlatSharedImageGallery struct { - Subscription *string `mapstructure:"subscription" cty:"subscription" hcl:"subscription"` - ResourceGroup *string `mapstructure:"resource_group" cty:"resource_group" hcl:"resource_group"` - GalleryName *string `mapstructure:"gallery_name" cty:"gallery_name" hcl:"gallery_name"` - ImageName *string `mapstructure:"image_name" cty:"image_name" hcl:"image_name"` - ImageVersion *string `mapstructure:"image_version" required:"false" cty:"image_version" hcl:"image_version"` -} - -// FlatMapstructure returns a new FlatSharedImageGallery. -// FlatSharedImageGallery is an auto-generated flat version of SharedImageGallery. -// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*SharedImageGallery) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { - return new(FlatSharedImageGallery) -} - -// HCL2Spec returns the hcl spec of a SharedImageGallery. -// This spec is used by HCL to read the fields of SharedImageGallery. -// The decoded values from this spec will then be applied to a FlatSharedImageGallery. -func (*FlatSharedImageGallery) HCL2Spec() map[string]hcldec.Spec { - s := map[string]hcldec.Spec{ - "subscription": &hcldec.AttrSpec{Name: "subscription", Type: cty.String, Required: false}, - "resource_group": &hcldec.AttrSpec{Name: "resource_group", Type: cty.String, Required: false}, - "gallery_name": &hcldec.AttrSpec{Name: "gallery_name", Type: cty.String, Required: false}, - "image_name": &hcldec.AttrSpec{Name: "image_name", Type: cty.String, Required: false}, - "image_version": &hcldec.AttrSpec{Name: "image_version", Type: cty.String, Required: false}, - } - return s -} - -// FlatSharedImageGalleryDestination is an auto-generated flat version of SharedImageGalleryDestination. -// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. -type FlatSharedImageGalleryDestination struct { - SigDestinationSubscription *string `mapstructure:"subscription" cty:"subscription" hcl:"subscription"` - SigDestinationResourceGroup *string `mapstructure:"resource_group" cty:"resource_group" hcl:"resource_group"` - SigDestinationGalleryName *string `mapstructure:"gallery_name" cty:"gallery_name" hcl:"gallery_name"` - SigDestinationImageName *string `mapstructure:"image_name" cty:"image_name" hcl:"image_name"` - SigDestinationImageVersion *string `mapstructure:"image_version" cty:"image_version" hcl:"image_version"` - SigDestinationReplicationRegions []string `mapstructure:"replication_regions" cty:"replication_regions" hcl:"replication_regions"` - SigDestinationStorageAccountType *string `mapstructure:"storage_account_type" cty:"storage_account_type" hcl:"storage_account_type"` -} - -// FlatMapstructure returns a new FlatSharedImageGalleryDestination. -// FlatSharedImageGalleryDestination is an auto-generated flat version of SharedImageGalleryDestination. -// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*SharedImageGalleryDestination) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { - return new(FlatSharedImageGalleryDestination) -} - -// HCL2Spec returns the hcl spec of a SharedImageGalleryDestination. -// This spec is used by HCL to read the fields of SharedImageGalleryDestination. -// The decoded values from this spec will then be applied to a FlatSharedImageGalleryDestination. -func (*FlatSharedImageGalleryDestination) HCL2Spec() map[string]hcldec.Spec { - s := map[string]hcldec.Spec{ - "subscription": &hcldec.AttrSpec{Name: "subscription", Type: cty.String, Required: false}, - "resource_group": &hcldec.AttrSpec{Name: "resource_group", Type: cty.String, Required: false}, - "gallery_name": &hcldec.AttrSpec{Name: "gallery_name", Type: cty.String, Required: false}, - "image_name": &hcldec.AttrSpec{Name: "image_name", Type: cty.String, Required: false}, - "image_version": &hcldec.AttrSpec{Name: "image_version", Type: cty.String, Required: false}, - "replication_regions": &hcldec.AttrSpec{Name: "replication_regions", Type: cty.List(cty.String), Required: false}, - "storage_account_type": &hcldec.AttrSpec{Name: "storage_account_type", Type: cty.String, Required: false}, - } - return s -} diff --git a/builder/azure/arm/config_test.go b/builder/azure/arm/config_test.go deleted file mode 100644 index 83b51f937..000000000 --- a/builder/azure/arm/config_test.go +++ /dev/null @@ -1,2188 +0,0 @@ -package arm - -import ( - "fmt" - "testing" - "time" - - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2018-04-01/compute" - "github.com/google/go-cmp/cmp" - sdkconfig "github.com/hashicorp/packer-plugin-sdk/template/config" - "github.com/hashicorp/packer/builder/azure/common/constants" -) - -// List of configuration parameters that are required by the ARM builder. -var requiredConfigValues = []string{ - "capture_name_prefix", - "capture_container_name", - "client_id", - "client_secret", - "image_offer", - "image_publisher", - "image_sku", - "location", - "os_type", - "storage_account", - "resource_group_name", - "subscription_id", -} - -func TestConfigShouldProvideReasonableDefaultValues(t *testing.T) { - var c Config - _, err := c.Prepare(getArmBuilderConfiguration(), getPackerConfiguration()) - - if err != nil { - t.Error("Expected configuration creation to succeed, but it failed!\n") - t.Fatalf(" errors: %s\n", err) - } - - if c.UserName == "" { - t.Error("Expected 'UserName' to be populated, but it was empty!") - } - - if c.VMSize == "" { - t.Error("Expected 'VMSize' to be populated, but it was empty!") - } - - if c.ClientConfig.ObjectID != "" { - t.Errorf("Expected 'ObjectID' to be nil, but it was '%s'!", c.ClientConfig.ObjectID) - } - - if c.managedImageStorageAccountType == "" { - t.Errorf("Expected 'managedImageStorageAccountType' to be populated, but it was empty!") - } - - if c.diskCachingType == "" { - t.Errorf("Expected 'diskCachingType' to be populated, but it was empty!") - } -} - -func TestConfigUserNameOverride(t *testing.T) { - builderValues := getArmBuilderConfiguration() - builderValues["ssh_username"] = "override_username" - builderValues["communicator"] = "ssh" - - var c Config - _, err := c.Prepare(builderValues, getPackerConfiguration()) - - if err != nil { - t.Fatalf("newConfig failed: %s", err) - } - - // SSH comm - if c.Password != c.tmpAdminPassword { - t.Errorf("Expected 'Password' to be set to generated password, but found %q!", c.Password) - } - if c.Comm.SSHPassword != "" { - t.Errorf("Expected 'c.Comm.SSHPassword' to be empty, but found %q!", c.Comm.SSHPassword) - } - if c.UserName != "override_username" { - t.Errorf("Expected 'UserName' to be set to 'override_username', but found %q!", c.UserName) - } - if c.Comm.SSHUsername != "override_username" { - t.Errorf("Expected 'c.Comm.SSHUsername' to be set to 'override_username', but found %q!", c.Comm.SSHUsername) - } - - // Winrm comm - c = Config{} - builderValues = getArmBuilderConfiguration() - builderValues["communicator"] = "winrm" - builderValues["winrm_username"] = "override_winrm_username" - _, err = c.Prepare(builderValues, getPackerConfiguration()) - if err != nil { - t.Fatalf("newConfig failed: %s", err) - } - if c.Password != c.tmpAdminPassword { - t.Errorf("Expected 'Password' to be set to generated password, but found %q!", c.Password) - } - if c.Comm.WinRMPassword != c.tmpAdminPassword { - t.Errorf("Expected 'c.Comm.WinRMPassword' to be set to generated password, but found %q!", c.Comm.WinRMPassword) - } - if c.UserName != "override_winrm_username" { - t.Errorf("Expected 'UserName' to be set to 'override_winrm_username', but found %q!", c.UserName) - } - if c.Comm.WinRMUser != "override_winrm_username" { - t.Errorf("Expected 'c.Comm.WinRMUser' to be set to 'override_winrm_username', but found %q!", c.Comm.WinRMUser) - } -} -func TestConfigShouldBeAbleToOverrideDefaultedValues(t *testing.T) { - builderValues := getArmBuilderConfiguration() - builderValues["ssh_password"] = "override_password" - builderValues["ssh_username"] = "override_username" - builderValues["vm_size"] = "override_vm_size" - builderValues["communicator"] = "ssh" - builderValues["managed_image_storage_account_type"] = "Premium_LRS" - builderValues["disk_caching_type"] = "None" - - var c Config - _, err := c.Prepare(builderValues, getPackerConfiguration()) - - if err != nil { - t.Fatalf("newConfig failed: %s", err) - } - - if c.VMSize != "override_vm_size" { - t.Errorf("Expected 'vm_size' to be set to 'override_vm_size', but found %q!", c.VMSize) - } - - if c.managedImageStorageAccountType != compute.StorageAccountTypesPremiumLRS { - t.Errorf("Expected 'managed_image_storage_account_type' to be set to 'Premium_LRS', but found %q!", c.managedImageStorageAccountType) - } - - if c.diskCachingType != compute.CachingTypesNone { - t.Errorf("Expected 'disk_caching_type' to be set to 'None', but found %q!", c.diskCachingType) - } - - // SSH comm - if c.Password != "override_password" { - t.Errorf("Expected 'Password' to be set to 'override_password', but found %q!", c.Password) - } - if c.Comm.SSHPassword != "override_password" { - t.Errorf("Expected 'c.Comm.SSHPassword' to be set to 'override_password', but found %q!", c.Comm.SSHPassword) - } - if c.UserName != "override_username" { - t.Errorf("Expected 'UserName' to be set to 'override_username', but found %q!", c.UserName) - } - if c.Comm.SSHUsername != "override_username" { - t.Errorf("Expected 'c.Comm.SSHUsername' to be set to 'override_username', but found %q!", c.Comm.SSHUsername) - } - - // Winrm comm - c = Config{} - builderValues = getArmBuilderConfiguration() - builderValues["communicator"] = "winrm" - builderValues["winrm_password"] = "Override_winrm_password1" - builderValues["winrm_username"] = "override_winrm_username" - _, err = c.Prepare(builderValues, getPackerConfiguration()) - if err != nil { - t.Fatalf("newConfig failed: %s", err) - } - if c.Password != "Override_winrm_password1" { - t.Errorf("Expected 'Password' to be set to 'Override_winrm_password1', but found %q!", c.Password) - } - if c.Comm.WinRMPassword != "Override_winrm_password1" { - t.Errorf("Expected 'c.Comm.WinRMPassword' to be set to 'Override_winrm_password1', but found %q!", c.Comm.WinRMPassword) - } - if c.UserName != "override_winrm_username" { - t.Errorf("Expected 'UserName' to be set to 'override_winrm_username', but found %q!", c.UserName) - } - if c.Comm.WinRMUser != "override_winrm_username" { - t.Errorf("Expected 'c.Comm.WinRMUser' to be set to 'override_winrm_username', but found %q!", c.Comm.WinRMUser) - } -} - -func TestConfigShouldDefaultVMSizeToStandardA1(t *testing.T) { - var c Config - c.Prepare(getArmBuilderConfiguration(), getPackerConfiguration()) - - if c.VMSize != "Standard_A1" { - t.Errorf("Expected 'VMSize' to default to 'Standard_A1', but got '%s'.", c.VMSize) - } -} - -func TestConfigShouldDefaultImageVersionToLatest(t *testing.T) { - var c Config - c.Prepare(getArmBuilderConfiguration(), getPackerConfiguration()) - - if c.ImageVersion != "latest" { - t.Errorf("Expected 'ImageVersion' to default to 'latest', but got '%s'.", c.ImageVersion) - } -} - -func TestConfigShouldNotDefaultImageVersionIfCustomImage(t *testing.T) { - config := map[string]string{ - "capture_name_prefix": "ignore", - "capture_container_name": "ignore", - "location": "ignore", - "image_url": "ignore", - "storage_account": "ignore", - "resource_group_name": "ignore", - "subscription_id": "ignore", - "os_type": constants.Target_Linux, - "communicator": "none", - } - - var c Config - c.Prepare(config, getPackerConfiguration()) - if c.ImageVersion != "" { - t.Errorf("Expected 'ImageVersion' to empty, but got '%s'.", c.ImageVersion) - } -} - -func TestConfigShouldNormalizeOSTypeCase(t *testing.T) { - config := map[string]string{ - "capture_name_prefix": "ignore", - "capture_container_name": "ignore", - "location": "ignore", - "image_url": "ignore", - "storage_account": "ignore", - "resource_group_name": "ignore", - "subscription_id": "ignore", - "communicator": "none", - } - - os_types := map[string][]string{ - constants.Target_Linux: {"linux", "LiNuX"}, - constants.Target_Windows: {"windows", "WiNdOWs"}, - } - - for k, v := range os_types { - for _, os_type := range v { - config["os_type"] = os_type - var c Config - _, err := c.Prepare(config, getPackerConfiguration()) - if err != nil { - t.Fatalf("Expected config to accept the value %q, but it did not", os_type) - } - - if c.OSType != k { - t.Fatalf("Expected config to normalize the value %q to %q, but it did not", os_type, constants.Target_Linux) - } - } - } - - bad_os_types := []string{"", "does-not-exist"} - for _, os_type := range bad_os_types { - config["os_type"] = os_type - var c Config - _, err := c.Prepare(config, getPackerConfiguration()) - if err == nil { - t.Fatalf("Expected config to not accept the value %q, but it did", os_type) - } - } -} - -func TestConfigShouldRejectCustomImageAndMarketPlace(t *testing.T) { - config := map[string]string{ - "capture_name_prefix": "ignore", - "capture_container_name": "ignore", - "location": "ignore", - "image_url": "ignore", - "resource_group_name": "ignore", - "storage_account": "ignore", - "subscription_id": "ignore", - "os_type": constants.Target_Linux, - "communicator": "none", - } - packerConfiguration := getPackerConfiguration() - marketPlace := []string{"image_publisher", "image_offer", "image_sku"} - - for _, x := range marketPlace { - config[x] = "ignore" - var c Config - _, err := c.Prepare(config, packerConfiguration) - if err == nil { - t.Errorf("Expected Config to reject image_url and %s, but it did not", x) - } - } -} - -func TestConfigVirtualNetworkNameIsOptional(t *testing.T) { - config := map[string]string{ - "capture_name_prefix": "ignore", - "capture_container_name": "ignore", - "location": "ignore", - "image_url": "ignore", - "storage_account": "ignore", - "resource_group_name": "ignore", - "subscription_id": "ignore", - "os_type": constants.Target_Linux, - "communicator": "none", - "virtual_network_name": "MyVirtualNetwork", - } - - var c Config - c.Prepare(config, getPackerConfiguration()) - if c.VirtualNetworkName != "MyVirtualNetwork" { - t.Errorf("Expected Config to set virtual_network_name to MyVirtualNetwork, but got %q", c.VirtualNetworkName) - } - if c.VirtualNetworkResourceGroupName != "" { - t.Errorf("Expected Config to leave virtual_network_resource_group_name to '', but got %q", c.VirtualNetworkResourceGroupName) - } - if c.VirtualNetworkSubnetName != "" { - t.Errorf("Expected Config to leave virtual_network_subnet_name to '', but got %q", c.VirtualNetworkSubnetName) - } -} - -// The user can pass the value virtual_network_resource_group_name to avoid the lookup of -// a virtual network's resource group, or to help with disambiguation. The value should -// only be set if virtual_network_name was set. -func TestConfigVirtualNetworkResourceGroupNameMustBeSetWithVirtualNetworkName(t *testing.T) { - config := map[string]string{ - "capture_name_prefix": "ignore", - "capture_container_name": "ignore", - "location": "ignore", - "image_url": "ignore", - "storage_account": "ignore", - "resource_group_name": "ignore", - "subscription_id": "ignore", - "os_type": constants.Target_Linux, - "communicator": "none", - "virtual_network_resource_group_name": "MyVirtualNetworkRG", - } - - var c Config - _, err := c.Prepare(config, getPackerConfiguration()) - if err == nil { - t.Error("Expected Config to reject virtual_network_resource_group_name, if virtual_network_name is not set.") - } -} - -// The user can pass the value virtual_network_subnet_name to avoid the lookup of -// a virtual network subnet's name, or to help with disambiguation. The value should -// only be set if virtual_network_name was set. -func TestConfigVirtualNetworkSubnetNameMustBeSetWithVirtualNetworkName(t *testing.T) { - config := map[string]string{ - "capture_name_prefix": "ignore", - "capture_container_name": "ignore", - "location": "ignore", - "image_url": "ignore", - "storage_account": "ignore", - "resource_group_name": "ignore", - "subscription_id": "ignore", - "os_type": constants.Target_Linux, - "communicator": "none", - "virtual_network_subnet_name": "MyVirtualNetworkRG", - } - - var c Config - _, err := c.Prepare(config, getPackerConfiguration()) - if err == nil { - t.Error("Expected Config to reject virtual_network_subnet_name, if virtual_network_name is not set.") - } -} - -func TestConfigAllowedInboundIpAddressesIsOptional(t *testing.T) { - config := map[string]string{ - "capture_name_prefix": "ignore", - "capture_container_name": "ignore", - "location": "ignore", - "image_url": "ignore", - "storage_account": "ignore", - "resource_group_name": "ignore", - "subscription_id": "ignore", - "os_type": constants.Target_Linux, - "communicator": "none", - "virtual_network_name": "MyVirtualNetwork", - } - - var c Config - _, err := c.Prepare(config, getPackerConfiguration()) - if err != nil { - t.Fatal(err) - } - if c.AllowedInboundIpAddresses != nil { - t.Errorf("Expected Config to set allowed_inbound_ip_addresses to nil, but got %v", c.AllowedInboundIpAddresses) - } -} - -func TestConfigShouldAcceptCorrectInboundIpAddresses(t *testing.T) { - ipValue0 := "127.0.0.1" - ipValue1 := "127.0.0.2" - cidrValue2 := "192.168.100.0/24" - cidrValue3 := "10.10.1.16/32" - config := map[string]interface{}{ - "capture_name_prefix": "ignore", - "capture_container_name": "ignore", - "location": "ignore", - "image_url": "ignore", - "storage_account": "ignore", - "resource_group_name": "ignore", - "subscription_id": "ignore", - "os_type": constants.Target_Linux, - "communicator": "none", - } - - config["allowed_inbound_ip_addresses"] = ipValue0 - var c Config - _, err := c.Prepare(config, getPackerConfiguration()) - if err != nil { - t.Fatal(err) - } - if c.AllowedInboundIpAddresses == nil || len(c.AllowedInboundIpAddresses) != 1 || - c.AllowedInboundIpAddresses[0] != ipValue0 { - t.Errorf("Expected 'allowed_inbound_ip_addresses' to have one element (%s), but got '%v'.", ipValue0, c.AllowedInboundIpAddresses) - } - - config["allowed_inbound_ip_addresses"] = cidrValue2 - _, err = c.Prepare(config, getPackerConfiguration()) - if err != nil { - t.Fatal(err) - } - if c.AllowedInboundIpAddresses == nil || len(c.AllowedInboundIpAddresses) != 1 || - c.AllowedInboundIpAddresses[0] != cidrValue2 { - t.Errorf("Expected 'allowed_inbound_ip_addresses' to have one element (%s), but got '%v'.", cidrValue2, c.AllowedInboundIpAddresses) - } - - config["allowed_inbound_ip_addresses"] = []string{ipValue0, cidrValue2, ipValue1, cidrValue3} - _, err = c.Prepare(config, getPackerConfiguration()) - if err != nil { - t.Fatal(err) - } - if c.AllowedInboundIpAddresses == nil || len(c.AllowedInboundIpAddresses) != 4 || - c.AllowedInboundIpAddresses[0] != ipValue0 || c.AllowedInboundIpAddresses[1] != cidrValue2 || - c.AllowedInboundIpAddresses[2] != ipValue1 || c.AllowedInboundIpAddresses[3] != cidrValue3 { - t.Errorf("Expected 'allowed_inbound_ip_addresses' to have four elements (%s %s %s %s), but got '%v'.", ipValue0, cidrValue2, ipValue1, - cidrValue3, c.AllowedInboundIpAddresses) - } -} - -func TestConfigShouldRejectIncorrectInboundIpAddresses(t *testing.T) { - config := map[string]interface{}{ - "capture_name_prefix": "ignore", - "capture_container_name": "ignore", - "location": "ignore", - "image_url": "ignore", - "storage_account": "ignore", - "resource_group_name": "ignore", - "subscription_id": "ignore", - "os_type": constants.Target_Linux, - "communicator": "none", - } - - config["allowed_inbound_ip_addresses"] = []string{"127.0.0.1", "127.0.0.two"} - var c Config - _, err := c.Prepare(config, getPackerConfiguration()) - if err == nil { - t.Errorf("Expected configuration creation to fail, but it succeeded with the malformed allowed_inbound_ip_addresses set to %v", c.AllowedInboundIpAddresses) - } - - config["allowed_inbound_ip_addresses"] = []string{"192.168.100.1000/24", "10.10.1.16/32"} - _, err = c.Prepare(config, getPackerConfiguration()) - if err == nil { - // 192.168.100.1000/24 is invalid - t.Errorf("Expected configuration creation to fail, but it succeeded with the malformed allowed_inbound_ip_addresses set to %v", c.AllowedInboundIpAddresses) - } -} - -func TestConfigShouldRejectInboundIpAddressesWithVirtualNetwork(t *testing.T) { - config := map[string]interface{}{ - "capture_name_prefix": "ignore", - "capture_container_name": "ignore", - "location": "ignore", - "image_url": "ignore", - "storage_account": "ignore", - "resource_group_name": "ignore", - "subscription_id": "ignore", - "os_type": constants.Target_Linux, - "communicator": "none", - "allowed_inbound_ip_addresses": "127.0.0.1", - } - - var c Config - _, err := c.Prepare(config, getPackerConfiguration()) - if err != nil { - t.Fatal(err) - } - - config["virtual_network_name"] = "some_vnet_name" - _, err = c.Prepare(config, getPackerConfiguration()) - if err == nil { - t.Errorf("Expected configuration creation to fail, but it succeeded with allowed_inbound_ip_addresses and virtual_network_name both specified") - } -} - -func TestConfigShouldDefaultToPublicCloud(t *testing.T) { - var c Config - c.Prepare(getArmBuilderConfiguration(), getPackerConfiguration()) - - if c.ClientConfig.CloudEnvironmentName != "Public" { - t.Errorf("Expected 'CloudEnvironmentName' to default to 'Public', but got '%s'.", c.ClientConfig.CloudEnvironmentName) - } - - if c.ClientConfig.CloudEnvironment() == nil || c.ClientConfig.CloudEnvironment().Name != "AzurePublicCloud" { - t.Errorf("Expected 'cloudEnvironment' to be set to 'AzurePublicCloud', but got '%s'.", c.ClientConfig.CloudEnvironment()) - } -} - -func TestConfigInstantiatesCorrectAzureEnvironment(t *testing.T) { - config := map[string]string{ - "capture_name_prefix": "ignore", - "capture_container_name": "ignore", - "image_offer": "ignore", - "image_publisher": "ignore", - "image_sku": "ignore", - "location": "ignore", - "storage_account": "ignore", - "resource_group_name": "ignore", - "subscription_id": "ignore", - "os_type": constants.Target_Linux, - "communicator": "none", - } - - // user input is fun :) - var table = []struct { - name string - environmentName string - }{ - {"China", "AzureChinaCloud"}, - {"ChinaCloud", "AzureChinaCloud"}, - {"AzureChinaCloud", "AzureChinaCloud"}, - {"aZuReChInAcLoUd", "AzureChinaCloud"}, - - {"USGovernment", "AzureUSGovernmentCloud"}, - {"USGovernmentCloud", "AzureUSGovernmentCloud"}, - {"AzureUSGovernmentCloud", "AzureUSGovernmentCloud"}, - {"aZuReUsGoVeRnMeNtClOuD", "AzureUSGovernmentCloud"}, - - {"Public", "AzurePublicCloud"}, - {"PublicCloud", "AzurePublicCloud"}, - {"AzurePublicCloud", "AzurePublicCloud"}, - {"aZuRePuBlIcClOuD", "AzurePublicCloud"}, - } - - packerConfiguration := getPackerConfiguration() - - for _, x := range table { - config["cloud_environment_name"] = x.name - var c Config - _, err := c.Prepare(config, packerConfiguration) - if err != nil { - t.Fatal(err) - } - - if c.ClientConfig.CloudEnvironment() == nil || c.ClientConfig.CloudEnvironment().Name != x.environmentName { - t.Errorf("Expected 'cloudEnvironment' to be set to '%s', but got '%s'.", x.environmentName, c.ClientConfig.CloudEnvironment()) - } - } -} - -func TestUserShouldProvideRequiredValues(t *testing.T) { - builderValues := getArmBuilderConfiguration() - - // Ensure we can successfully create a config. - var c Config - _, err := c.Prepare(builderValues, getPackerConfiguration()) - if err != nil { - t.Error("Expected configuration creation to succeed, but it failed!\n") - t.Fatalf(" -> %+v\n", builderValues) - } - - // Take away a required element, and ensure construction fails. - for _, v := range requiredConfigValues { - originalValue := builderValues[v] - delete(builderValues, v) - - var c Config - _, err := c.Prepare(builderValues, getPackerConfiguration()) - if err == nil { - t.Error("Expected configuration creation to fail, but it succeeded!\n") - t.Fatalf(" -> %+v\n", builderValues) - } - - builderValues[v] = originalValue - } -} - -func TestSystemShouldDefineRuntimeValues(t *testing.T) { - var c Config - c.Prepare(getArmBuilderConfiguration(), getPackerConfiguration()) - - if c.Password == "" { - t.Errorf("Expected Password to not be empty, but it was '%s'!", c.Password) - } - - if c.tmpComputeName == "" { - t.Errorf("Expected tmpComputeName to not be empty, but it was '%s'!", c.tmpComputeName) - } - - if c.tmpDeploymentName == "" { - t.Errorf("Expected tmpDeploymentName to not be empty, but it was '%s'!", c.tmpDeploymentName) - } - - if c.tmpResourceGroupName == "" { - t.Errorf("Expected tmpResourceGroupName to not be empty, but it was '%s'!", c.tmpResourceGroupName) - } - - if c.tmpOSDiskName == "" { - t.Errorf("Expected tmpOSDiskName to not be empty, but it was '%s'!", c.tmpOSDiskName) - } - - if c.tmpDataDiskName == "" { - t.Errorf("Expected tmpDataDiskName to not be empty, but it was '%s'!", c.tmpDataDiskName) - } - - if c.tmpNsgName == "" { - t.Errorf("Expected tmpNsgName to not be empty, but it was '%s'!", c.tmpNsgName) - } -} - -func TestConfigShouldTransformToVirtualMachineCaptureParameters(t *testing.T) { - var c Config - c.Prepare(getArmBuilderConfiguration(), getPackerConfiguration()) - parameters := c.toVirtualMachineCaptureParameters() - - if *parameters.DestinationContainerName != c.CaptureContainerName { - t.Errorf("Expected DestinationContainerName to be equal to config's CaptureContainerName, but they were '%s' and '%s' respectively.", *parameters.DestinationContainerName, c.CaptureContainerName) - } - - if *parameters.VhdPrefix != c.CaptureNamePrefix { - t.Errorf("Expected DestinationContainerName to be equal to config's CaptureContainerName, but they were '%s' and '%s' respectively.", *parameters.VhdPrefix, c.CaptureNamePrefix) - } - - if *parameters.OverwriteVhds != false { - t.Error("Expected OverwriteVhds to be false, but it was not.") - } -} - -func TestConfigShouldSupportPackersConfigElements(t *testing.T) { - var c Config - _, err := c.Prepare( - getArmBuilderConfiguration(), - getPackerConfiguration(), - getPackerCommunicatorConfiguration()) - - if err != nil { - t.Fatal(err) - } - - if c.Comm.SSHTimeout != 1*time.Hour { - t.Errorf("Expected Comm.SSHTimeout to be a duration of an hour, but got '%s' instead.", c.Comm.SSHTimeout) - } - - if c.Comm.WinRMTimeout != 2*time.Hour { - t.Errorf("Expected Comm.WinRMTimeout to be a durationof two hours, but got '%s' instead.", c.Comm.WinRMTimeout) - } -} - -func TestWinRMConfigShouldSetRoundTripDecorator(t *testing.T) { - config := getArmBuilderConfiguration() - config["communicator"] = "winrm" - config["winrm_username"] = "username" - config["winrm_password"] = "Password123" - - var c Config - _, err := c.Prepare(config, getPackerConfiguration()) - if err != nil { - t.Fatal(err) - } - - if c.Comm.WinRMTransportDecorator == nil { - t.Error("Expected WinRMTransportDecorator to be set, but it was nil") - } -} - -func TestUserDeviceLoginIsEnabledForLinux(t *testing.T) { - config := map[string]string{ - "capture_name_prefix": "ignore", - "capture_container_name": "ignore", - "image_offer": "ignore", - "image_publisher": "ignore", - "image_sku": "ignore", - "location": "ignore", - "storage_account": "ignore", - "resource_group_name": "ignore", - "subscription_id": "ignore", - "os_type": constants.Target_Linux, - "communicator": "none", - } - - var c Config - _, err := c.Prepare(config, getPackerConfiguration()) - if err != nil { - t.Fatalf("failed to use device login for Linux: %s", err) - } -} - -func TestConfigShouldRejectMalformedCaptureNamePrefix(t *testing.T) { - config := map[string]string{ - "capture_container_name": "ignore", - "image_offer": "ignore", - "image_publisher": "ignore", - "image_sku": "ignore", - "location": "ignore", - "storage_account": "ignore", - "resource_group_name": "ignore", - "subscription_id": "ignore", - "communicator": "none", - // Does not matter for this test case, just pick one. - "os_type": constants.Target_Linux, - } - - wellFormedCaptureNamePrefix := []string{ - "packer", - "AbcdefghijklmnopqrstuvwX", - "hyphen-hyphen", - "0leading-number", - "v1.core.local", - } - - for _, x := range wellFormedCaptureNamePrefix { - config["capture_name_prefix"] = x - var c Config - _, err := c.Prepare(config, getPackerConfiguration()) - - if err != nil { - t.Errorf("Expected test to pass, but it failed with the well-formed capture_name_prefix set to %q.", x) - } - } - - malformedCaptureNamePrefix := []string{ - "-leading-hyphen", - "trailing-hyphen-", - "trailing-period.", - "_leading-underscore", - "punc-!@#$%^&*()_+-=-punc", - "There-are-too-many-characters-in-the-name-and-the-limit-is-twenty-four", - } - - for _, x := range malformedCaptureNamePrefix { - config["capture_name_prefix"] = x - var c Config - _, err := c.Prepare(config, getPackerConfiguration()) - - if err == nil { - t.Errorf("Expected test to fail, but it succeeded with the malformed capture_name_prefix set to %q.", x) - } - } -} - -func TestConfigShouldRejectMalformedCaptureContainerName(t *testing.T) { - config := map[string]string{ - "capture_name_prefix": "ignore", - "image_offer": "ignore", - "image_publisher": "ignore", - "image_sku": "ignore", - "location": "ignore", - "storage_account": "ignore", - "resource_group_name": "ignore", - "subscription_id": "ignore", - "communicator": "none", - // Does not matter for this test case, just pick one. - "os_type": constants.Target_Linux, - } - - wellFormedCaptureContainerName := []string{ - "0leading", - "aleading", - "hype-hyphen", - "abcdefghijklmnopqrstuvwxyz0123456789-abcdefghijklmnopqrstuvwxyz", // 63 characters - } - - for _, x := range wellFormedCaptureContainerName { - config["capture_container_name"] = x - var c Config - _, err := c.Prepare(config, getPackerConfiguration()) - - if err != nil { - t.Errorf("Expected test to pass, but it failed with the well-formed capture_container_name set to %q.", x) - } - } - - malformedCaptureContainerName := []string{ - "No-Capitals", - "double--hyphens", - "-leading-hyphen", - "trailing-hyphen-", - "punc-!@#$%^&*()_+-=-punc", - "there-are-over-63-characters-in-this-string-and-that-is-a-bad-container-name", - } - - for _, x := range malformedCaptureContainerName { - config["capture_container_name"] = x - var c Config - _, err := c.Prepare(config, getPackerConfiguration()) - - if err == nil { - t.Errorf("Expected test to fail, but it succeeded with the malformed capture_container_name set to %q.", x) - } - } -} - -func TestConfigShouldRejectMalformedManagedImageOSDiskSnapshotName(t *testing.T) { - config := map[string]interface{}{ - "image_offer": "ignore", - "image_publisher": "ignore", - "image_sku": "ignore", - "location": "ignore", - "subscription_id": "ignore", - "communicator": "none", - "managed_image_resource_group_name": "ignore", - "managed_image_name": "ignore", - "managed_image_os_disk_snapshot_name": "ignore", - // Does not matter for this test case, just pick one. - "os_type": constants.Target_Linux, - } - - wellFormedManagedImageOSDiskSnapshotName := []string{ - "AbcdefghijklmnopqrstuvwX", - "underscore_underscore", - "0leading_number", - "really_loooooooooooooooooooooooooooooooooooooooooooooooooong", - } - - for _, x := range wellFormedManagedImageOSDiskSnapshotName { - config["managed_image_os_disk_snapshot_name"] = x - var c Config - _, err := c.Prepare(config, getPackerConfiguration()) - - if err != nil { - t.Errorf("Expected test to pass, but it failed with the well-formed managed_image_os_disk_snapshot_name set to %q.", x) - } - } - - malformedManagedImageOSDiskSnapshotName := []string{ - "-leading-hyphen", - "trailing-hyphen-", - "trailing-period.", - "punc-!@#$%^&*()_+-=-punc", - "really_looooooooooooooooooooooooooooooooooooooooooooooooooooooong_exceeding_80_char_limit", - } - - for _, x := range malformedManagedImageOSDiskSnapshotName { - config["managed_image_os_disk_snapshot_name"] = x - var c Config - _, err := c.Prepare(config, getPackerConfiguration()) - - if err == nil { - t.Errorf("Expected test to fail, but it succeeded with the malformed managed_image_os_disk_snapshot_name set to %q.", x) - } - } -} - -func TestConfigShouldRejectMalformedManagedImageDataDiskSnapshotPrefix(t *testing.T) { - config := map[string]interface{}{ - "image_offer": "ignore", - "image_publisher": "ignore", - "image_sku": "ignore", - "location": "ignore", - "subscription_id": "ignore", - "communicator": "none", - "managed_image_resource_group_name": "ignore", - "managed_image_name": "ignore", - "managed_image_data_disk_snapshot_prefix": "ignore", - // Does not matter for this test case, just pick one. - "os_type": constants.Target_Linux, - } - - wellFormedManagedImageDataDiskSnapshotPrefix := []string{ - "min_ten_chars", - "AbcdefghijklmnopqrstuvwX", - "underscore_underscore", - "0leading_number", - "less_than_sixty_characters", - } - - for _, x := range wellFormedManagedImageDataDiskSnapshotPrefix { - config["managed_image_data_disk_snapshot_prefix"] = x - var c Config - _, err := c.Prepare(config, getPackerConfiguration()) - - if err != nil { - t.Errorf("Expected test to pass, but it failed with the well-formed managed_image_data_disk_snapshot_prefix set to %q.", x) - } - } - - malformedManagedImageDataDiskSnapshotPrefix := []string{ - "-leading-hyphen", - "trailing-hyphen-", - "trailing-period.", - "punc-!@#$%^&*()_+-=-punc", - "really_looooooooooooooooooooooooooooooooooooooooooooooooooooooong_exceeding_60_char_limit", - } - - for _, x := range malformedManagedImageDataDiskSnapshotPrefix { - config["managed_image_data_disk_snapshot_prefix"] = x - var c Config - _, err := c.Prepare(config, getPackerConfiguration()) - - if err == nil { - t.Errorf("Expected test to fail, but it succeeded with the malformed managed_image_data_disk_snapshot_prefix set to %q.", x) - } - } -} - -func TestConfigShouldAcceptTags(t *testing.T) { - config := map[string]interface{}{ - "capture_name_prefix": "ignore", - "capture_container_name": "ignore", - "image_offer": "ignore", - "image_publisher": "ignore", - "image_sku": "ignore", - "location": "ignore", - "storage_account": "ignore", - "resource_group_name": "ignore", - "subscription_id": "ignore", - "communicator": "none", - // Does not matter for this test case, just pick one. - "os_type": constants.Target_Linux, - "azure_tags": map[string]string{ - "tag01": "value01", - "tag02": "value02", - }, - } - - c := Config{ - AzureTag: sdkconfig.NameValues{ - {Name: "tag03", Value: "value03"}, - }, - } - _, err := c.Prepare(config, getPackerConfiguration()) - if err != nil { - t.Fatal(err) - } - - if diff := cmp.Diff(c.AzureTags, map[string]string{ - "tag01": "value01", - "tag02": "value02", - "tag03": "value03", - }); diff != "" { - t.Fatalf("unexpected azure tags: %s", diff) - } -} - -func TestConfigShouldAcceptTag(t *testing.T) { - config := map[string]interface{}{ - "capture_name_prefix": "ignore", - "capture_container_name": "ignore", - "image_offer": "ignore", - "image_publisher": "ignore", - "image_sku": "ignore", - "location": "ignore", - "storage_account": "ignore", - "resource_group_name": "ignore", - "subscription_id": "ignore", - "communicator": "none", - // Does not matter for this test case, just pick one. - "os_type": constants.Target_Linux, - } - - c := Config{ - AzureTag: sdkconfig.NameValues{ - {Name: "tag03", Value: "value03"}, - }, - } - _, err := c.Prepare(config, getPackerConfiguration()) - if err != nil { - t.Fatal(err) - } - - if diff := cmp.Diff(c.AzureTags, map[string]string{ - "tag03": "value03", - }); diff != "" { - t.Fatalf("unexpected azure tags: %s", diff) - } -} - -func TestConfigShouldRejectTagsInExcessOf15AcceptTags(t *testing.T) { - tooManyTags := map[string]string{} - for i := 0; i < 16; i++ { - tooManyTags[fmt.Sprintf("tag%.2d", i)] = "ignored" - } - - config := map[string]interface{}{ - "capture_name_prefix": "ignore", - "capture_container_name": "ignore", - "image_offer": "ignore", - "image_publisher": "ignore", - "image_sku": "ignore", - "location": "ignore", - "storage_account": "ignore", - "resource_group_name": "ignore", - "subscription_id": "ignore", - "communicator": "none", - // Does not matter for this test case, just pick one. - "os_type": constants.Target_Linux, - "azure_tags": tooManyTags, - } - - var c Config - _, err := c.Prepare(config, getPackerConfiguration()) - - if err == nil { - t.Fatal("expected config to reject based on an excessive amount of tags (> 15)") - } -} - -func TestConfigShouldRejectExcessiveTagNameLength(t *testing.T) { - nameTooLong := make([]byte, 513) - for i := range nameTooLong { - nameTooLong[i] = 'a' - } - - tags := map[string]string{} - tags[string(nameTooLong)] = "ignored" - - config := map[string]interface{}{ - "capture_name_prefix": "ignore", - "capture_container_name": "ignore", - "image_offer": "ignore", - "image_publisher": "ignore", - "image_sku": "ignore", - "location": "ignore", - "storage_account": "ignore", - "resource_group_name": "ignore", - "subscription_id": "ignore", - "communicator": "none", - // Does not matter for this test case, just pick one. - "os_type": constants.Target_Linux, - "azure_tags": tags, - } - - var c Config - _, err := c.Prepare(config, getPackerConfiguration()) - if err == nil { - t.Fatal("expected config to reject tag name based on length (> 512)") - } -} - -func TestConfigShouldRejectExcessiveTagValueLength(t *testing.T) { - valueTooLong := make([]byte, 257) - for i := range valueTooLong { - valueTooLong[i] = 'a' - } - - tags := map[string]string{} - tags["tag01"] = string(valueTooLong) - - config := map[string]interface{}{ - "capture_name_prefix": "ignore", - "capture_container_name": "ignore", - "image_offer": "ignore", - "image_publisher": "ignore", - "image_sku": "ignore", - "location": "ignore", - "storage_account": "ignore", - "resource_group_name": "ignore", - "subscription_id": "ignore", - "communicator": "none", - // Does not matter for this test case, just pick one. - "os_type": constants.Target_Linux, - "azure_tags": tags, - } - - var c Config - _, err := c.Prepare(config, getPackerConfiguration()) - if err == nil { - t.Fatal("expected config to reject tag value based on length (> 256)") - } -} - -func TestConfigZoneResilientShouldDefaultToFalse(t *testing.T) { - config := map[string]interface{}{ - "managed_image_name": "ignore", - "managed_image_resource_group_name": "ignore", - "build_resource_group_name": "ignore", - "image_publisher": "igore", - "image_offer": "ignore", - "image_sku": "ignore", - "os_type": "linux", - } - - var c Config - _, err := c.Prepare(config, getPackerConfiguration()) - if err != nil { - t.Fatal(err) - } - - p := c.toImageParameters() - if *p.ImageProperties.StorageProfile.ZoneResilient { - t.Fatal("expected zone resilient default to be false") - } -} - -func TestConfigZoneResilientSetFromConfig(t *testing.T) { - config := map[string]interface{}{ - "managed_image_name": "ignore", - "managed_image_resource_group_name": "ignore", - "build_resource_group_name": "ignore", - "image_publisher": "igore", - "image_offer": "ignore", - "image_sku": "ignore", - "os_type": "linux", - "managed_image_zone_resilient": true, - } - - var c Config - _, err := c.Prepare(config, getPackerConfiguration()) - if err != nil { - t.Fatal(err) - } - - p := c.toImageParameters() - if *p.ImageProperties.StorageProfile.ZoneResilient == false { - t.Fatal("expected managed image zone resilient to be true from config") - } -} - -func TestConfigShouldRejectMissingCustomDataFile(t *testing.T) { - config := map[string]interface{}{ - "capture_name_prefix": "ignore", - "capture_container_name": "ignore", - "image_offer": "ignore", - "image_publisher": "ignore", - "image_sku": "ignore", - "location": "ignore", - "storage_account": "ignore", - "resource_group_name": "ignore", - "subscription_id": "ignore", - "communicator": "none", - // Does not matter for this test case, just pick one. - "os_type": constants.Target_Linux, - "custom_data_file": "/this/file/does/not/exist", - } - - var c Config - _, err := c.Prepare(config, getPackerConfiguration()) - if err == nil { - t.Fatal("expected config to reject missing custom data file") - } -} - -func TestConfigShouldRejectManagedImageOSDiskSnapshotNameWithoutManagedImageName(t *testing.T) { - config := map[string]interface{}{ - "image_offer": "ignore", - "image_publisher": "ignore", - "image_sku": "ignore", - "location": "ignore", - "subscription_id": "ignore", - "communicator": "none", - "managed_image_resource_group_name": "ignore", - "managed_image_os_disk_snapshot_name": "ignore", - // Does not matter for this test case, just pick one. - "os_type": constants.Target_Linux, - } - - var c Config - _, err := c.Prepare(config, getPackerConfiguration()) - if err == nil { - t.Fatal("expected config to reject Managed Image build with OS disk snapshot name but without managed image name") - } -} - -func TestConfigShouldRejectManagedImageOSDiskSnapshotNameWithoutManagedImageResourceGroupName(t *testing.T) { - config := map[string]interface{}{ - "image_offer": "ignore", - "image_publisher": "ignore", - "image_sku": "ignore", - "location": "ignore", - "subscription_id": "ignore", - "communicator": "none", - "managed_image_name": "ignore", - "managed_image_os_disk_snapshot_name": "ignore", - // Does not matter for this test case, just pick one. - "os_type": constants.Target_Linux, - } - - var c Config - _, err := c.Prepare(config, getPackerConfiguration()) - if err == nil { - t.Fatal("expected config to reject Managed Image build with OS disk snapshot name but without managed image resource group name") - } -} - -func TestConfigShouldRejectImageDataDiskSnapshotPrefixWithoutManagedImageName(t *testing.T) { - config := map[string]interface{}{ - "image_offer": "ignore", - "image_publisher": "ignore", - "image_sku": "ignore", - "location": "ignore", - "subscription_id": "ignore", - "communicator": "none", - "managed_image_resource_group_name": "ignore", - "managed_image_data_disk_snapshot_prefix": "ignore", - // Does not matter for this test case, just pick one. - "os_type": constants.Target_Linux, - } - - var c Config - _, err := c.Prepare(config, getPackerConfiguration()) - if err == nil { - t.Fatal("expected config to reject Managed Image build with data disk snapshot prefix but without managed image name") - } -} - -func TestConfigShouldRejectImageDataDiskSnapshotPrefixWithoutManagedImageResourceGroupName(t *testing.T) { - config := map[string]interface{}{ - "image_offer": "ignore", - "image_publisher": "ignore", - "image_sku": "ignore", - "location": "ignore", - "subscription_id": "ignore", - "communicator": "none", - "managed_image_name": "ignore", - "managed_image_data_disk_snapshot_prefix": "ignore", - // Does not matter for this test case, just pick one. - "os_type": constants.Target_Linux, - } - - var c Config - _, err := c.Prepare(config, getPackerConfiguration()) - if err == nil { - t.Fatal("expected config to reject Managed Image build with data disk snapshot prefix but without managed image resource group name") - } -} - -func TestConfigShouldAcceptManagedImageOSDiskSnapshotNameAndManagedImageDataDiskSnapshotPrefix(t *testing.T) { - config := map[string]interface{}{ - "image_offer": "ignore", - "image_publisher": "ignore", - "image_sku": "ignore", - "location": "ignore", - "subscription_id": "ignore", - "communicator": "none", - "managed_image_resource_group_name": "ignore", - "managed_image_name": "ignore", - "managed_image_os_disk_snapshot_name": "ignore_ignore", - "managed_image_data_disk_snapshot_prefix": "ignore_ignore", - // Does not matter for this test case, just pick one. - "os_type": constants.Target_Linux, - } - - var c Config - _, err := c.Prepare(config, getPackerConfiguration()) - if err != nil { - t.Fatal("expected config to accept platform managed image build") - } -} - -func TestConfigShouldRejectManagedImageOSDiskSnapshotNameAndManagedImageDataDiskSnapshotPrefixWithCaptureContainerName(t *testing.T) { - config := map[string]interface{}{ - "image_offer": "ignore", - "image_publisher": "ignore", - "image_sku": "ignore", - "location": "ignore", - "subscription_id": "ignore", - "communicator": "none", - "capture_container_name": "ignore", - "managed_image_os_disk_snapshot_name": "ignore_ignore", - "managed_image_data_disk_snapshot_prefix": "ignore_ignore", - // Does not matter for this test case, just pick one. - "os_type": constants.Target_Linux, - } - - var c Config - _, err := c.Prepare(config, getPackerConfiguration()) - if err == nil { - t.Fatal("expected config to reject Managed Image build with data disk snapshot prefix and OS disk snapshot name with capture container name") - } -} - -func TestConfigShouldRejectManagedImageOSDiskSnapshotNameAndManagedImageDataDiskSnapshotPrefixWithCaptureNamePrefix(t *testing.T) { - config := map[string]interface{}{ - "image_offer": "ignore", - "image_publisher": "ignore", - "image_sku": "ignore", - "location": "ignore", - "subscription_id": "ignore", - "communicator": "none", - "capture_name_prefix": "ignore", - "managed_image_os_disk_snapshot_name": "ignore_ignore", - "managed_image_data_disk_snapshot_prefix": "ignore_ignore", - // Does not matter for this test case, just pick one. - "os_type": constants.Target_Linux, - } - - var c Config - _, err := c.Prepare(config, getPackerConfiguration()) - if err == nil { - t.Fatal("expected config to reject Managed Image build with data disk snapshot prefix and OS disk snapshot name with capture name prefix") - } -} - -func TestConfigShouldAcceptPlatformManagedImageBuild(t *testing.T) { - config := map[string]interface{}{ - "image_offer": "ignore", - "image_publisher": "ignore", - "image_sku": "ignore", - "location": "ignore", - "subscription_id": "ignore", - "communicator": "none", - "managed_image_resource_group_name": "ignore", - "managed_image_name": "ignore", - - // Does not matter for this test case, just pick one. - "os_type": constants.Target_Linux, - } - - var c Config - _, err := c.Prepare(config, getPackerConfiguration()) - if err != nil { - t.Fatal("expected config to accept platform managed image build") - } -} - -// If the user specified a build for a VHD and a Managed Image it should be rejected. -func TestConfigShouldRejectVhdAndManagedImageOutput(t *testing.T) { - config := map[string]interface{}{ - "image_offer": "ignore", - "image_publisher": "ignore", - "image_sku": "ignore", - "location": "ignore", - "subscription_id": "ignore", - "communicator": "none", - "capture_container_name": "ignore", - "capture_name_prefix": "ignore", - "managed_image_resource_group_name": "ignore", - "managed_image_name": "ignore", - - // Does not matter for this test case, just pick one. - "os_type": constants.Target_Linux, - } - - var c Config - _, err := c.Prepare(config, getPackerConfiguration()) - if err == nil { - t.Fatal("expected config to reject VHD and Managed Image build") - } -} - -// If the user specified a build of a VHD, but started with a managed image it should be rejected. -func TestConfigShouldRejectManagedImageSourceAndVhdOutput(t *testing.T) { - config := map[string]interface{}{ - "image_url": "ignore", - "location": "ignore", - "subscription_id": "ignore", - "communicator": "none", - "managed_image_resource_group_name": "ignore", - "managed_image_name": "ignore", - - // Does not matter for this test case, just pick one. - "os_type": constants.Target_Linux, - } - - var c Config - _, err := c.Prepare(config, getPackerConfiguration()) - if err == nil { - t.Fatal("expected config to reject VHD and Managed Image build") - } -} - -func TestConfigShouldRejectCustomAndPlatformManagedImageBuild(t *testing.T) { - config := map[string]interface{}{ - "custom_managed_image_resource_group_name": "ignore", - "custom_managed_image_name": "ignore", - "image_offer": "ignore", - "image_publisher": "ignore", - "image_sku": "ignore", - "location": "ignore", - "subscription_id": "ignore", - "communicator": "none", - "managed_image_resource_group_name": "ignore", - "managed_image_name": "ignore", - - // Does not matter for this test case, just pick one. - "os_type": constants.Target_Linux, - } - - var c Config - _, err := c.Prepare(config, getPackerConfiguration()) - if err == nil { - t.Fatal("expected config to reject custom and platform input for a managed image build") - } -} - -func TestConfigShouldRejectCustomAndImageUrlForManagedImageBuild(t *testing.T) { - config := map[string]interface{}{ - "image_url": "ignore", - "custom_managed_image_resource_group_name": "ignore", - "custom_managed_image_name": "ignore", - "location": "ignore", - "subscription_id": "ignore", - "communicator": "none", - "managed_image_resource_group_name": "ignore", - "managed_image_name": "ignore", - - // Does not matter for this test case, just pick one. - "os_type": constants.Target_Linux, - } - - var c Config - _, err := c.Prepare(config, getPackerConfiguration()) - if err == nil { - t.Fatal("expected config to reject custom and platform input for a managed image build") - } -} - -func TestConfigShouldRejectMalformedManageImageStorageAccountTypes(t *testing.T) { - config := map[string]interface{}{ - "custom_managed_image_resource_group_name": "ignore", - "custom_managed_image_name": "ignore", - "location": "ignore", - "subscription_id": "ignore", - "communicator": "none", - "managed_image_resource_group_name": "ignore", - "managed_image_name": "ignore", - "managed_image_storage_account_type": "--invalid--", - - // Does not matter for this test case, just pick one. - "os_type": constants.Target_Linux, - } - - var c Config - _, err := c.Prepare(config, getPackerConfiguration()) - if err == nil { - t.Fatal("expected config to reject custom and platform input for a managed image build") - } -} - -func TestConfigShouldRejectMalformedDiskCachingType(t *testing.T) { - config := map[string]interface{}{ - "custom_managed_image_resource_group_name": "ignore", - "custom_managed_image_name": "ignore", - "location": "ignore", - "subscription_id": "ignore", - "communicator": "none", - "managed_image_resource_group_name": "ignore", - "managed_image_name": "ignore", - "disk_caching_type": "--invalid--", - - // Does not matter for this test case, just pick one. - "os_type": constants.Target_Linux, - } - - var c Config - _, err := c.Prepare(config, getPackerConfiguration()) - if err == nil { - t.Fatal("expected config to reject custom and platform input for a managed image build") - } -} - -func TestConfigShouldAcceptManagedImageStorageAccountTypes(t *testing.T) { - config := map[string]interface{}{ - "custom_managed_image_resource_group_name": "ignore", - "custom_managed_image_name": "ignore", - "location": "ignore", - "subscription_id": "ignore", - "communicator": "none", - "managed_image_resource_group_name": "ignore", - "managed_image_name": "ignore", - - // Does not matter for this test case, just pick one. - "os_type": constants.Target_Linux, - } - - storage_account_types := []string{"Premium_LRS", "Standard_LRS"} - - for _, x := range storage_account_types { - config["managed_image_storage_account_type"] = x - var c Config - _, err := c.Prepare(config, getPackerConfiguration()) - if err != nil { - t.Fatalf("expected config to accept a managed_image_storage_account_type of %q", x) - } - } -} - -func TestConfigShouldAcceptDiskCachingTypes(t *testing.T) { - config := map[string]interface{}{ - "custom_managed_image_resource_group_name": "ignore", - "custom_managed_image_name": "ignore", - "location": "ignore", - "subscription_id": "ignore", - "communicator": "none", - "managed_image_resource_group_name": "ignore", - "managed_image_name": "ignore", - - // Does not matter for this test case, just pick one. - "os_type": constants.Target_Linux, - } - - storage_account_types := []string{"None", "ReadOnly", "ReadWrite"} - - for _, x := range storage_account_types { - config["disk_caching_type"] = x - var c Config - _, err := c.Prepare(config, getPackerConfiguration()) - if err != nil { - t.Fatalf("expected config to accept a disk_caching_type of %q", x) - } - } -} - -func TestConfigShouldRejectTempAndBuildResourceGroupName(t *testing.T) { - config := map[string]interface{}{ - "capture_name_prefix": "ignore", - "capture_container_name": "ignore", - "image_offer": "ignore", - "image_publisher": "ignore", - "image_sku": "ignore", - "location": "ignore", - "storage_account": "ignore", - "resource_group_name": "ignore", - "subscription_id": "ignore", - "communicator": "none", - - // custom may define one or the other, but not both - "temp_resource_group_name": "rgn00", - "build_resource_group_name": "rgn00", - } - - var c Config - _, err := c.Prepare(config, getPackerConfiguration()) - if err == nil { - t.Fatal("expected config to reject the use of both temp_resource_group_name and build_resource_group_name") - } -} - -func TestConfigShouldRejectInvalidResourceGroupNames(t *testing.T) { - config := map[string]interface{}{ - "capture_name_prefix": "ignore", - "capture_container_name": "ignore", - "image_offer": "ignore", - "image_publisher": "ignore", - "image_sku": "ignore", - "location": "ignore", - "storage_account": "ignore", - "resource_group_name": "ignore", - "subscription_id": "ignore", - "communicator": "none", - "os_type": "linux", - } - - tests := []struct { - name string - ok bool - }{ - // The Good - {"packer-Resource-Group-jt2j3fc", true}, - {"My", true}, - {"My-(with-parens)-Resource-Group", true}, - - // The Bad - {"My Resource Group", false}, - {"My-Resource-Group-", false}, - {"My.Resource.Group.", false}, - - // The Ugly - {"My!@#!@#%$%yM", false}, - {" ", false}, - {"My10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", false}, - } - - settings := []string{"temp_resource_group_name", "build_resource_group_name"} - - for _, x := range settings { - for _, y := range tests { - config[x] = y.name - - var c Config - _, err := c.Prepare(config, getPackerConfiguration()) - if !y.ok && err == nil { - t.Errorf("expected config to reject %q for setting %q", y.name, x) - } else if y.ok && err != nil { - t.Errorf("expected config to accept %q for setting %q", y.name, x) - } - } - - delete(config, "location") // not valid for build_resource_group_name - delete(config, x) - } -} - -func TestConfigShouldRejectManagedDiskNames(t *testing.T) { - config := map[string]interface{}{ - "image_offer": "ignore", - "image_publisher": "ignore", - "image_sku": "ignore", - "location": "ignore", - "subscription_id": "ignore", - "communicator": "none", - "os_type": "linux", - "managed_image_name": "ignore", - "managed_image_resource_group_name": "ignore", - } - - testsResourceGroupNames := []struct { - name string - ok bool - }{ - // The Good - {"packer-Resource-Group-jt2j3fc", true}, - {"My", true}, - {"My-(with-parens)-Resource-Group", true}, - - // The Bad - {"My Resource Group", false}, - {"My-Resource-Group-", false}, - {"My.Resource.Group.", false}, - - // The Ugly - {"My!@#!@#%$%yM", false}, - {" ", false}, - {"My10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", false}, - } - - settingUnderTest := "managed_image_resource_group_name" - for _, y := range testsResourceGroupNames { - config[settingUnderTest] = y.name - - var c Config - _, err := c.Prepare(config, getPackerConfiguration()) - if !y.ok && err == nil { - t.Errorf("expected config to reject %q for setting %q", y.name, settingUnderTest) - } else if y.ok && err != nil { - t.Errorf("expected config to accept %q for setting %q", y.name, settingUnderTest) - } - } - - config["managed_image_resource_group_name"] = "ignored" - - testNames := []struct { - name string - ok bool - }{ - // The Good - {"ManagedDiskName", true}, - {"Managed-Disk-Name", true}, - {"My33", true}, - - // The Bad - {"Managed Disk Name", false}, - {"Managed-Disk-Name-", false}, - {"Managed.Disk.Name.", false}, - - // The Ugly - {"My!@#!@#%$%yM", false}, - {" ", false}, - {"My10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", false}, - } - - settingUnderTest = "managed_image_name" - for _, y := range testNames { - config[settingUnderTest] = y.name - - var c Config - _, err := c.Prepare(config, getPackerConfiguration()) - if !y.ok && err == nil { - t.Logf("expected config to reject %q for setting %q", y.name, settingUnderTest) - } else if y.ok && err != nil { - t.Logf("expected config to accept %q for setting %q", y.name, settingUnderTest) - } - } -} - -func TestConfigAdditionalDiskDefaultIsNil(t *testing.T) { - var c Config - c.Prepare(getArmBuilderConfiguration(), getPackerConfiguration()) - if c.AdditionalDiskSize != nil { - t.Errorf("Expected Config to not have a set of additional disks, but got a non nil value") - } -} - -func TestConfigAdditionalDiskOverrideDefault(t *testing.T) { - config := map[string]string{ - "capture_name_prefix": "ignore", - "capture_container_name": "ignore", - "location": "ignore", - "image_url": "ignore", - "storage_account": "ignore", - "resource_group_name": "ignore", - "subscription_id": "ignore", - "os_type": constants.Target_Linux, - "communicator": "none", - } - - diskconfig := map[string][]int32{ - "disk_additional_size": {32, 64}, - } - - var c Config - c.Prepare(config, diskconfig, getPackerConfiguration()) - if c.AdditionalDiskSize == nil { - t.Errorf("Expected Config to have a set of additional disks, but got nil") - } - if len(c.AdditionalDiskSize) != 2 { - t.Errorf("Expected Config to have a 2 additional disks, but got %d additional disks", len(c.AdditionalDiskSize)) - } - if c.AdditionalDiskSize[0] != 32 { - t.Errorf("Expected Config to have the first additional disks of size 32Gb, but got %dGb", c.AdditionalDiskSize[0]) - } - if c.AdditionalDiskSize[1] != 64 { - t.Errorf("Expected Config to have the second additional disks of size 64Gb, but got %dGb", c.AdditionalDiskSize[1]) - } -} - -// Test that configuration handles plan info -// -// The use of plan info requires that the following three properties are set. -// -// 1. plan_name -// 2. plan_product -// 3. plan_publisher -func TestPlanInfoConfiguration(t *testing.T) { - config := map[string]interface{}{ - "capture_name_prefix": "ignore", - "capture_container_name": "ignore", - "image_offer": "ignore", - "image_publisher": "ignore", - "image_sku": "ignore", - "location": "ignore", - "storage_account": "ignore", - "resource_group_name": "ignore", - "subscription_id": "ignore", - "os_type": "linux", - "communicator": "none", - } - - planInfo := map[string]string{ - "plan_name": "--plan-name--", - } - config["plan_info"] = planInfo - - var c Config - _, err := c.Prepare(config, getPackerConfiguration()) - if err == nil { - t.Fatal("expected config to reject the use of plan_name without plan_product and plan_publisher") - } - - planInfo["plan_product"] = "--plan-product--" - _, err = c.Prepare(config, getPackerConfiguration()) - if err == nil { - t.Fatal("expected config to reject the use of plan_name and plan_product without plan_publisher") - } - - planInfo["plan_publisher"] = "--plan-publisher--" - _, err = c.Prepare(config, getPackerConfiguration()) - if err != nil { - t.Fatalf("expected config to accept a complete plan configuration: %s", err) - } - - if c.PlanInfo.PlanName != "--plan-name--" { - t.Fatalf("Expected PlanName to be '--plan-name--', but got %q", c.PlanInfo.PlanName) - } - if c.PlanInfo.PlanProduct != "--plan-product--" { - t.Fatalf("Expected PlanProduct to be '--plan-product--', but got %q", c.PlanInfo.PlanProduct) - } - if c.PlanInfo.PlanPublisher != "--plan-publisher--" { - t.Fatalf("Expected PlanPublisher to be '--plan-publisher--, but got %q", c.PlanInfo.PlanPublisher) - } -} - -func TestPlanInfoPromotionCode(t *testing.T) { - config := map[string]interface{}{ - "capture_name_prefix": "ignore", - "capture_container_name": "ignore", - "image_offer": "ignore", - "image_publisher": "ignore", - "image_sku": "ignore", - "location": "ignore", - "storage_account": "ignore", - "resource_group_name": "ignore", - "subscription_id": "ignore", - "os_type": "linux", - "communicator": "none", - "plan_info": map[string]string{ - "plan_name": "--plan-name--", - "plan_product": "--plan-product--", - "plan_publisher": "--plan-publisher--", - "plan_promotion_code": "--plan-promotion-code--", - }, - } - - var c Config - _, err := c.Prepare(config, getPackerConfiguration()) - if err != nil { - t.Fatalf("expected config to accept plan_info configuration, but got %s", err) - } - - if c.PlanInfo.PlanName != "--plan-name--" { - t.Fatalf("Expected PlanName to be '--plan-name--', but got %q", c.PlanInfo.PlanName) - } - if c.PlanInfo.PlanProduct != "--plan-product--" { - t.Fatalf("Expected PlanProduct to be '--plan-product--', but got %q", c.PlanInfo.PlanProduct) - } - if c.PlanInfo.PlanPublisher != "--plan-publisher--" { - t.Fatalf("Expected PlanPublisher to be '--plan-publisher--, but got %q", c.PlanInfo.PlanPublisher) - } - if c.PlanInfo.PlanPromotionCode != "--plan-promotion-code--" { - t.Fatalf("Expected PlanPublisher to be '--plan-promotion-code----, but got %q", c.PlanInfo.PlanPromotionCode) - } -} - -// plan_info defines 3 or 4 tags based on plan data. -// The user can define up to 15 tags. If the combination of these two -// exceeds the max tag amount, the builder should reject the configuration. -func TestPlanInfoTooManyTagsErrors(t *testing.T) { - exactMaxNumberOfTags := map[string]string{} - for i := 0; i < 15; i++ { - exactMaxNumberOfTags[fmt.Sprintf("tag%.2d", i)] = "ignored" - } - - config := map[string]interface{}{ - "capture_name_prefix": "ignore", - "capture_container_name": "ignore", - "image_offer": "ignore", - "image_publisher": "ignore", - "image_sku": "ignore", - "location": "ignore", - "storage_account": "ignore", - "resource_group_name": "ignore", - "subscription_id": "ignore", - "os_type": "linux", - "communicator": "none", - "azure_tags": exactMaxNumberOfTags, - "plan_info": map[string]string{ - "plan_name": "--plan-name--", - "plan_product": "--plan-product--", - "plan_publisher": "--plan-publisher--", - "plan_promotion_code": "--plan-promotion-code--", - }, - } - - var c Config - _, err := c.Prepare(config, getPackerConfiguration()) - if err == nil { - t.Fatal("expected config to reject configuration due to excess tags") - } -} - -// The Azure builder creates temporary resources, but the user has some control over -// these values. This test asserts those values are controllable by the user. -func TestConfigShouldAllowTempNameOverrides(t *testing.T) { - config := map[string]interface{}{ - "image_offer": "ignore", - "image_publisher": "ignore", - "image_sku": "ignore", - "location": "ignore", - "subscription_id": "ignore", - "communicator": "none", - "os_type": "linux", - "managed_image_name": "ignore", - "managed_image_resource_group_name": "ignore", - "temp_resource_group_name": "myTempResourceGroupName", - "temp_compute_name": "myTempComputeName", - } - - var c Config - _, err := c.Prepare(config, getPackerConfiguration()) - if err != nil { - t.Errorf("newConfig failed with %q", err) - } - - if c.TempResourceGroupName != "myTempResourceGroupName" { - t.Errorf("expected TempResourceGroupName to be %q, but got %q", "myTempResourceGroupName", c.TempResourceGroupName) - } - if c.tmpResourceGroupName != "myTempResourceGroupName" { - t.Errorf("expected tmpResourceGroupName to be %q, but got %q", "myTempResourceGroupName", c.tmpResourceGroupName) - } - - if c.TempComputeName != "myTempComputeName" { - t.Errorf("expected TempComputeName to be %q, but got %q", "myTempComputeName", c.TempComputeName) - } - if c.tmpComputeName != "myTempComputeName" { - t.Errorf("expected tmpComputeName to be %q, but got %q", "myTempComputeName", c.tmpResourceGroupName) - } -} - -func TestConfigShouldAllowAsyncResourceGroupOverride(t *testing.T) { - config := map[string]interface{}{ - "image_offer": "ignore", - "image_publisher": "ignore", - "image_sku": "ignore", - "location": "ignore", - "subscription_id": "ignore", - "communicator": "none", - "os_type": "linux", - "managed_image_name": "ignore", - "managed_image_resource_group_name": "ignore", - "async_resourcegroup_delete": "true", - } - - var c Config - _, err := c.Prepare(config, getPackerConfiguration()) - if err != nil { - t.Errorf("newConfig failed with %q", err) - } - - if c.AsyncResourceGroupDelete != true { - t.Errorf("expected async_resourcegroup_delete to be %q, but got %t", "async_resourcegroup_delete", c.AsyncResourceGroupDelete) - } -} -func TestConfigShouldAllowAsyncResourceGroupOverrideNoValue(t *testing.T) { - config := map[string]interface{}{ - "image_offer": "ignore", - "image_publisher": "ignore", - "image_sku": "ignore", - "location": "ignore", - "subscription_id": "ignore", - "communicator": "none", - "os_type": "linux", - "managed_image_name": "ignore", - "managed_image_resource_group_name": "ignore", - } - - var c Config - _, err := c.Prepare(config, getPackerConfiguration()) - if err != nil { - t.Errorf("newConfig failed with %q", err) - } - - if c.AsyncResourceGroupDelete != false { - t.Errorf("expected async_resourcegroup_delete to be %q, but got %t", "async_resourcegroup_delete", c.AsyncResourceGroupDelete) - } -} -func TestConfigShouldAllowAsyncResourceGroupOverrideBadValue(t *testing.T) { - config := map[string]interface{}{ - "image_offer": "ignore", - "image_publisher": "ignore", - "image_sku": "ignore", - "location": "ignore", - "subscription_id": "ignore", - "communicator": "none", - "os_type": "linux", - "managed_image_name": "ignore", - "managed_image_resource_group_name": "ignore", - "async_resourcegroup_delete": "asdasda", - } - - var c Config - _, err := c.Prepare(config, getPackerConfiguration()) - if err != nil { - t.Log("newConfig failed which is expected ", err) - } - -} -func TestConfigShouldAllowSharedImageGalleryOptions(t *testing.T) { - config := map[string]interface{}{ - "location": "ignore", - "subscription_id": "ignore", - "os_type": "linux", - "shared_image_gallery": map[string]string{ - "subscription": "ignore", - "resource_group": "ignore", - "gallery_name": "ignore", - "image_name": "ignore", - "image_version": "ignore", - }, - } - - var c Config - _, err := c.Prepare(config, getPackerConfiguration()) - if err == nil { - t.Log("expected config to accept Shared Image Gallery options", err) - } - -} - -func TestConfigShouldRejectSharedImageGalleryInvalidStorageAccountType(t *testing.T) { - config := map[string]interface{}{ - "location": "ignore", - "subscription_id": "ignore", - "os_type": "linux", - "shared_image_gallery": map[string]string{ - "subscription": "ignore", - "resource_group": "ignore", - "gallery_name": "ignore", - "image_name": "ignore", - "image_version": "ignore", - "storage_account_type": "--invalid--", - }, - } - - var c Config - _, err := c.Prepare(config, getPackerConfiguration()) - if err != nil { - t.Log("config Shared Image Gallery with unsupported storage account type failed which is expected", err) - } - -} - -func TestConfigShouldRejectSharedImageGalleryWithVhdTarget(t *testing.T) { - config := map[string]interface{}{ - "location": "ignore", - "subscription_id": "ignore", - "os_type": "linux", - "shared_image_gallery": map[string]string{ - "subscription": "ignore", - "resource_group": "ignore", - "gallery_name": "ignore", - "image_name": "ignore", - "image_version": "ignore", - }, - "resource_group_name": "ignore", - "storage_account": "ignore", - "capture_container_name": "ignore", - "capture_name_prefix": "ignore", - } - - var c Config - _, err := c.Prepare(config, getPackerConfiguration()) - if err != nil { - t.Log("expected an error if Shared Image Gallery source is used with VHD target", err) - } -} - -func Test_GivenZoneNotSupportingResiliency_ConfigValidate_ShouldWarn(t *testing.T) { - builderValues := getArmBuilderConfiguration() - builderValues["managed_image_zone_resilient"] = "true" - builderValues["location"] = "ukwest" - - var c Config - _, err := c.Prepare(builderValues, getPackerConfiguration()) - if err != nil { - t.Errorf("newConfig failed with %q", err) - } - - var m = "" - c.validateLocationZoneResiliency(func(s string) { m = s }) - - if m != "WARNING: Zone resiliency may not be supported in ukwest, checkout the docs at https://docs.microsoft.com/en-us/azure/availability-zones/" { - t.Errorf("warning message not as expected: %s", m) - } -} - -func Test_GivenZoneSupportingResiliency_ConfigValidate_ShouldNotWarn(t *testing.T) { - builderValues := getArmBuilderConfiguration() - builderValues["managed_image_zone_resilient"] = "true" - builderValues["location"] = "westeurope" - - var c Config - _, err := c.Prepare(builderValues, getPackerConfiguration()) - if err != nil { - t.Errorf("newConfig failed with %q", err) - } - - var m = "" - c.validateLocationZoneResiliency(func(s string) { m = s }) - - if m != "" { - t.Errorf("warning message not as expected: %s", m) - } -} - -func TestConfig_PrepareProvidedWinRMPassword(t *testing.T) { - config := getArmBuilderConfiguration() - config["communicator"] = "winrm" - - var c Config - tc := []struct { - name string - password string - shouldFail bool - }{ - { - name: "password should be longer than 8 characters", - password: "packer", - shouldFail: true, - }, - { - name: "password should be shorter than 123 characters", - password: "1Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", - shouldFail: true, - }, - { - name: "password should have valid size but only lower and upper case letters", - password: "AAAbbbCCC", - shouldFail: true, - }, - { - name: "password should have valid size but only digits and upper case letters", - password: "AAA12345", - shouldFail: true, - }, - { - name: "password should have valid size, digits, upper and lower case letters", - password: "AAA12345bbb", - shouldFail: false, - }, - { - name: "password should have valid size, digits, special characters and lower case letters", - password: "//12345bbb", - shouldFail: false, - }, - } - - for _, tt := range tc { - t.Run(tt.name, func(t *testing.T) { - config["winrm_password"] = tt.password - _, err := c.Prepare(config) - fail := err != nil - if tt.shouldFail != fail { - t.Fatalf("bad: %s. Expected fail is: %t but it was %t", tt.name, tt.shouldFail, fail) - } - }) - } -} - -func getArmBuilderConfiguration() map[string]interface{} { - m := make(map[string]interface{}) - for _, v := range requiredConfigValues { - m[v] = "ignored00" - } - - m["communicator"] = "none" - m["os_type"] = constants.Target_Linux - return m -} - -func getArmBuilderConfigurationWithWindows() map[string]string { - m := make(map[string]string) - for _, v := range requiredConfigValues { - m[v] = "ignored00" - } - - m["object_id"] = "ignored00" - m["tenant_id"] = "ignored00" - m["winrm_username"] = "ignored00" - m["communicator"] = "winrm" - m["os_type"] = constants.Target_Windows - return m -} - -func getPackerConfiguration() interface{} { - config := map[string]interface{}{ - "packer_build_name": "azure-arm-vm", - "packer_builder_type": "azure-arm-vm", - "packer_debug": "false", - "packer_force": "false", - "packer_template_path": "/home/jenkins/azure-arm-vm/template.json", - } - - return config -} - -func getPackerCommunicatorConfiguration() map[string]string { - config := map[string]string{ - "ssh_timeout": "1h", - "winrm_timeout": "2h", - } - - return config -} - -func getPackerSSHPasswordCommunicatorConfiguration() map[string]string { - config := map[string]string{ - "ssh_password": "superS3cret", - } - - return config -} - -func TestConfigShouldRejectMalformedUserAssignedManagedIdentities(t *testing.T) { - config := map[string]interface{}{ - "capture_name_prefix": "ignore", - "capture_container_name": "ignore", - "image_offer": "ignore", - "image_publisher": "ignore", - "image_sku": "ignore", - "location": "ignore", - "storage_account": "ignore", - "resource_group_name": "ignore", - "subscription_id": "ignore", - "communicator": "none", - // Does not matter for this test case, just pick one. - "os_type": constants.Target_Linux, - } - - config["user_assigned_managed_identities"] = []string{"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg1/providers/Microsoft.ManagedIdentity/userAssignedIdentities/id"} - var c Config - if _, err := c.Prepare(config, getPackerConfiguration()); err != nil { - t.Error("Expected test to pass, but it failed with the well-formed user_assigned_managed_identities.") - } - - malformedUserAssignedManagedIdentityResourceIds := []string{ - "not_a_resource_id", - "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg1/providers/Microsoft.ManagedIdentity/userAssignedIdentities/", - "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg1/providers/Microsoft.Compute/images/im", - "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg1/providers/Microsoft.ManagedIdentity/userAssignedIdentitie/id", - } - - for _, x := range malformedUserAssignedManagedIdentityResourceIds { - config["user_assigned_managed_identities"] = x - var c Config - if _, err := c.Prepare(config, getPackerConfiguration()); err == nil { - t.Errorf("Expected test to fail, but it succeeded with the malformed user_assigned_managed_identities set to %q.", x) - } - } -} diff --git a/builder/azure/arm/inspector.go b/builder/azure/arm/inspector.go deleted file mode 100644 index e760323d5..000000000 --- a/builder/azure/arm/inspector.go +++ /dev/null @@ -1,71 +0,0 @@ -package arm - -import ( - "bytes" - "io/ioutil" - "log" - "net/http" - - "io" - - "github.com/Azure/go-autorest/autorest" - "github.com/Azure/go-autorest/autorest/azure" - "github.com/hashicorp/packer/builder/azure/common/logutil" -) - -func chop(data []byte, maxlen int64) string { - s := string(data) - if int64(len(s)) > maxlen { - s = s[:maxlen] + "..." - } - return s -} - -func handleBody(body io.ReadCloser, maxlen int64) (io.ReadCloser, string) { - if body == nil { - return nil, "" - } - - defer body.Close() - - b, err := ioutil.ReadAll(body) - if err != nil { - return nil, "" - } - - return ioutil.NopCloser(bytes.NewReader(b)), chop(b, maxlen) -} - -func withInspection(maxlen int64) autorest.PrepareDecorator { - return func(p autorest.Preparer) autorest.Preparer { - return autorest.PreparerFunc(func(r *http.Request) (*http.Request, error) { - body, bodyString := handleBody(r.Body, maxlen) - r.Body = body - - log.Print("Azure request", logutil.Fields{ - "method": r.Method, - "request": r.URL.String(), - "body": bodyString, - }) - return p.Prepare(r) - }) - } -} - -func byInspecting(maxlen int64) autorest.RespondDecorator { - return func(r autorest.Responder) autorest.Responder { - return autorest.ResponderFunc(func(resp *http.Response) error { - body, bodyString := handleBody(resp.Body, maxlen) - resp.Body = body - - log.Print("Azure response", logutil.Fields{ - "status": resp.Status, - "method": resp.Request.Method, - "request": resp.Request.URL.String(), - "x-ms-request-id": azure.ExtractRequestID(resp), - "body": bodyString, - }) - return r.Respond(resp) - }) - } -} diff --git a/builder/azure/arm/openssh_key_pair.go b/builder/azure/arm/openssh_key_pair.go deleted file mode 100644 index 5ab97c00e..000000000 --- a/builder/azure/arm/openssh_key_pair.go +++ /dev/null @@ -1,59 +0,0 @@ -package arm - -import ( - "crypto/rand" - "crypto/rsa" - "crypto/x509" - "encoding/base64" - "encoding/pem" - "fmt" - "time" - - "golang.org/x/crypto/ssh" -) - -const ( - KeySize = 2048 -) - -type OpenSshKeyPair struct { - privateKey *rsa.PrivateKey - publicKey ssh.PublicKey -} - -func NewOpenSshKeyPair() (*OpenSshKeyPair, error) { - return NewOpenSshKeyPairWithSize(KeySize) -} - -func NewOpenSshKeyPairWithSize(keySize int) (*OpenSshKeyPair, error) { - privateKey, err := rsa.GenerateKey(rand.Reader, keySize) - if err != nil { - return nil, err - } - - publicKey, err := ssh.NewPublicKey(&privateKey.PublicKey) - if err != nil { - return nil, err - } - - return &OpenSshKeyPair{ - privateKey: privateKey, - publicKey: publicKey, - }, nil -} - -func (s *OpenSshKeyPair) AuthorizedKey() string { - return fmt.Sprintf("%s %s packer Azure Deployment%s", - s.publicKey.Type(), - base64.StdEncoding.EncodeToString(s.publicKey.Marshal()), - time.Now().Format(time.RFC3339)) -} - -func (s *OpenSshKeyPair) PrivateKey() []byte { - privateKey := pem.EncodeToMemory(&pem.Block{ - Type: "RSA PRIVATE KEY", - Bytes: x509.MarshalPKCS1PrivateKey(s.privateKey), - }) - - return privateKey -} diff --git a/builder/azure/arm/openssh_key_pair_test.go b/builder/azure/arm/openssh_key_pair_test.go deleted file mode 100644 index 8dc9cebf2..000000000 --- a/builder/azure/arm/openssh_key_pair_test.go +++ /dev/null @@ -1,37 +0,0 @@ -package arm - -import ( - "testing" - - "golang.org/x/crypto/ssh" -) - -func TestFart(t *testing.T) { - -} - -func TestAuthorizedKeyShouldParse(t *testing.T) { - testSubject, err := NewOpenSshKeyPairWithSize(512) - if err != nil { - t.Fatalf("Failed to create a new OpenSSH key pair, err=%s.", err) - } - - authorizedKey := testSubject.AuthorizedKey() - - _, _, _, _, err = ssh.ParseAuthorizedKey([]byte(authorizedKey)) - if err != nil { - t.Fatalf("Failed to parse the authorized key, err=%s", err) - } -} - -func TestPrivateKeyShouldParse(t *testing.T) { - testSubject, err := NewOpenSshKeyPairWithSize(512) - if err != nil { - t.Fatalf("Failed to create a new OpenSSH key pair, err=%s.", err) - } - - _, err = ssh.ParsePrivateKey([]byte(testSubject.PrivateKey())) - if err != nil { - t.Fatalf("Failed to parse the private key, err=%s\n", err) - } -} diff --git a/builder/azure/arm/resource_resolver.go b/builder/azure/arm/resource_resolver.go deleted file mode 100644 index 8b89d78af..000000000 --- a/builder/azure/arm/resource_resolver.go +++ /dev/null @@ -1,141 +0,0 @@ -package arm - -// Code to resolve resources that are required by the API. These resources -// can most likely be resolved without asking the user, thereby reducing the -// amount of configuration they need to provide. -// -// Resource resolver differs from config retriever because resource resolver -// requires a client to communicate with the Azure API. A config retriever is -// used to determine values without use of a client. - -import ( - "context" - "fmt" - "strings" - - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2018-04-01/compute" -) - -type resourceResolver struct { - client *AzureClient - findVirtualNetworkResourceGroup func(*AzureClient, string) (string, error) - findVirtualNetworkSubnet func(*AzureClient, string, string) (string, error) -} - -func newResourceResolver(client *AzureClient) *resourceResolver { - return &resourceResolver{ - client: client, - findVirtualNetworkResourceGroup: findVirtualNetworkResourceGroup, - findVirtualNetworkSubnet: findVirtualNetworkSubnet, - } -} - -func (s *resourceResolver) Resolve(c *Config) error { - if s.shouldResolveResourceGroup(c) { - resourceGroupName, err := s.findVirtualNetworkResourceGroup(s.client, c.VirtualNetworkName) - if err != nil { - return err - } - - subnetName, err := s.findVirtualNetworkSubnet(s.client, resourceGroupName, c.VirtualNetworkName) - if err != nil { - return err - } - - c.VirtualNetworkResourceGroupName = resourceGroupName - c.VirtualNetworkSubnetName = subnetName - } - - if s.shouldResolveManagedImageName(c) { - image, err := findManagedImageByName(s.client, c.CustomManagedImageName, c.CustomManagedImageResourceGroupName) - if err != nil { - return err - } - - c.customManagedImageID = *image.ID - } - - return nil -} - -func (s *resourceResolver) shouldResolveResourceGroup(c *Config) bool { - return c.VirtualNetworkName != "" && c.VirtualNetworkResourceGroupName == "" -} - -func (s *resourceResolver) shouldResolveManagedImageName(c *Config) bool { - return c.CustomManagedImageName != "" -} - -func getResourceGroupNameFromId(id string) string { - // "/subscriptions/3f499422-dd76-4114-8859-86d526c9deb6/resourceGroups/packer-Resource-Group-yylnwsl30j/providers/... - xs := strings.Split(id, "/") - return xs[4] -} - -func findManagedImageByName(client *AzureClient, name, resourceGroupName string) (*compute.Image, error) { - images, err := client.ImagesClient.ListByResourceGroupComplete(context.TODO(), resourceGroupName) - if err != nil { - return nil, err - } - - for images.NotDone() { - image := images.Value() - if strings.EqualFold(name, *image.Name) { - return &image, nil - } - if err = images.Next(); err != nil { - return nil, err - } - } - - return nil, fmt.Errorf("Cannot find an image named '%s' in the resource group '%s'", name, resourceGroupName) -} - -func findVirtualNetworkResourceGroup(client *AzureClient, name string) (string, error) { - virtualNetworks, err := client.VirtualNetworksClient.ListAllComplete(context.TODO()) - if err != nil { - return "", err - } - - resourceGroupNames := make([]string, 0) - for virtualNetworks.NotDone() { - virtualNetwork := virtualNetworks.Value() - if strings.EqualFold(name, *virtualNetwork.Name) { - rgn := getResourceGroupNameFromId(*virtualNetwork.ID) - resourceGroupNames = append(resourceGroupNames, rgn) - } - if err = virtualNetworks.Next(); err != nil { - return "", err - } - } - - if len(resourceGroupNames) == 0 { - return "", fmt.Errorf("Cannot find a resource group with a virtual network called %q", name) - } - - if len(resourceGroupNames) > 1 { - return "", fmt.Errorf("Found multiple resource groups with a virtual network called %q, please use virtual_network_subnet_name and virtual_network_resource_group_name to disambiguate", name) - } - - return resourceGroupNames[0], nil -} - -func findVirtualNetworkSubnet(client *AzureClient, resourceGroupName string, name string) (string, error) { - subnets, err := client.SubnetsClient.List(context.TODO(), resourceGroupName, name) - if err != nil { - return "", err - } - - subnetList := subnets.Values() // only first page of subnets, but only interested in ==0 or >1 - - if len(subnetList) == 0 { - return "", fmt.Errorf("Cannot find a subnet in the resource group %q associated with the virtual network called %q", resourceGroupName, name) - } - - if len(subnetList) > 1 { - return "", fmt.Errorf("Found multiple subnets in the resource group %q associated with the virtual network called %q, please use virtual_network_subnet_name and virtual_network_resource_group_name to disambiguate", resourceGroupName, name) - } - - subnet := subnetList[0] - return *subnet.Name, nil -} diff --git a/builder/azure/arm/resource_resolver_test.go b/builder/azure/arm/resource_resolver_test.go deleted file mode 100644 index 68b227ee0..000000000 --- a/builder/azure/arm/resource_resolver_test.go +++ /dev/null @@ -1,79 +0,0 @@ -package arm - -import ( - "testing" -) - -func TestResourceResolverIgnoresEmptyVirtualNetworkName(t *testing.T) { - var c Config - c.Prepare(getArmBuilderConfiguration(), getPackerConfiguration()) - if c.VirtualNetworkName != "" { - t.Fatalf("Expected VirtualNetworkName to be empty by default") - } - - sut := newTestResourceResolver() - sut.findVirtualNetworkResourceGroup = nil // assert that this is not even called - sut.Resolve(&c) - - if c.VirtualNetworkName != "" { - t.Fatalf("Expected VirtualNetworkName to be empty") - } - if c.VirtualNetworkResourceGroupName != "" { - t.Fatalf("Expected VirtualNetworkResourceGroupName to be empty") - } -} - -// If the user fully specified the virtual network name and resource group then -// there is no need to do a lookup. -func TestResourceResolverIgnoresSetVirtualNetwork(t *testing.T) { - var c Config - c.Prepare(getArmBuilderConfiguration(), getPackerConfiguration()) - c.VirtualNetworkName = "--virtual-network-name--" - c.VirtualNetworkResourceGroupName = "--virtual-network-resource-group-name--" - c.VirtualNetworkSubnetName = "--virtual-network-subnet-name--" - - sut := newTestResourceResolver() - sut.findVirtualNetworkResourceGroup = nil // assert that this is not even called - sut.findVirtualNetworkSubnet = nil // assert that this is not even called - sut.Resolve(&c) - - if c.VirtualNetworkName != "--virtual-network-name--" { - t.Fatalf("Expected VirtualNetworkName to be --virtual-network-name--") - } - if c.VirtualNetworkResourceGroupName != "--virtual-network-resource-group-name--" { - t.Fatalf("Expected VirtualNetworkResourceGroupName to be --virtual-network-resource-group-name--") - } - if c.VirtualNetworkSubnetName != "--virtual-network-subnet-name--" { - t.Fatalf("Expected VirtualNetworkSubnetName to be --virtual-network-subnet-name--") - } -} - -// If the user set virtual network name then the code should resolve virtual network -// resource group name. -func TestResourceResolverSetVirtualNetworkResourceGroupName(t *testing.T) { - var c Config - c.Prepare(getArmBuilderConfiguration(), getPackerConfiguration()) - c.VirtualNetworkName = "--virtual-network-name--" - - sut := newTestResourceResolver() - sut.Resolve(&c) - - if c.VirtualNetworkResourceGroupName != "findVirtualNetworkResourceGroup is mocked" { - t.Fatalf("Expected VirtualNetworkResourceGroupName to be 'findVirtualNetworkResourceGroup is mocked'") - } - if c.VirtualNetworkSubnetName != "findVirtualNetworkSubnet is mocked" { - t.Fatalf("Expected findVirtualNetworkSubnet to be 'findVirtualNetworkSubnet is mocked'") - } -} - -func newTestResourceResolver() resourceResolver { - return resourceResolver{ - client: nil, - findVirtualNetworkResourceGroup: func(*AzureClient, string) (string, error) { - return "findVirtualNetworkResourceGroup is mocked", nil - }, - findVirtualNetworkSubnet: func(*AzureClient, string, string) (string, error) { - return "findVirtualNetworkSubnet is mocked", nil - }, - } -} diff --git a/builder/azure/arm/step.go b/builder/azure/arm/step.go deleted file mode 100644 index def6ebd72..000000000 --- a/builder/azure/arm/step.go +++ /dev/null @@ -1,20 +0,0 @@ -package arm - -import ( - "github.com/hashicorp/packer-plugin-sdk/multistep" - "github.com/hashicorp/packer/builder/azure/common/constants" -) - -func processStepResult( - err error, sayError func(error), state multistep.StateBag) multistep.StepAction { - - if err != nil { - state.Put(constants.Error, err) - sayError(err) - - return multistep.ActionHalt - } - - return multistep.ActionContinue - -} diff --git a/builder/azure/arm/step_capture_image.go b/builder/azure/arm/step_capture_image.go deleted file mode 100644 index fa65f8415..000000000 --- a/builder/azure/arm/step_capture_image.go +++ /dev/null @@ -1,120 +0,0 @@ -package arm - -import ( - "context" - "fmt" - - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2018-04-01/compute" - "github.com/hashicorp/packer-plugin-sdk/multistep" - packersdk "github.com/hashicorp/packer-plugin-sdk/packer" - "github.com/hashicorp/packer/builder/azure/common/constants" -) - -type StepCaptureImage struct { - client *AzureClient - generalizeVM func(resourceGroupName, computeName string) error - captureVhd func(ctx context.Context, resourceGroupName string, computeName string, parameters *compute.VirtualMachineCaptureParameters) error - captureManagedImage func(ctx context.Context, resourceGroupName string, computeName string, parameters *compute.Image) error - get func(client *AzureClient) *CaptureTemplate - say func(message string) - error func(e error) -} - -func NewStepCaptureImage(client *AzureClient, ui packersdk.Ui) *StepCaptureImage { - var step = &StepCaptureImage{ - client: client, - get: func(client *AzureClient) *CaptureTemplate { - return client.Template - }, - say: func(message string) { - ui.Say(message) - }, - error: func(e error) { - ui.Error(e.Error()) - }, - } - - step.generalizeVM = step.generalize - step.captureVhd = step.captureImage - step.captureManagedImage = step.captureImageFromVM - - return step -} - -func (s *StepCaptureImage) generalize(resourceGroupName string, computeName string) error { - _, err := s.client.Generalize(context.TODO(), resourceGroupName, computeName) - if err != nil { - s.say(s.client.LastError.Error()) - } - return err -} - -func (s *StepCaptureImage) captureImageFromVM(ctx context.Context, resourceGroupName string, imageName string, image *compute.Image) error { - f, err := s.client.ImagesClient.CreateOrUpdate(ctx, resourceGroupName, imageName, *image) - if err != nil { - s.say(s.client.LastError.Error()) - } - return f.WaitForCompletionRef(ctx, s.client.ImagesClient.Client) -} - -func (s *StepCaptureImage) captureImage(ctx context.Context, resourceGroupName string, computeName string, parameters *compute.VirtualMachineCaptureParameters) error { - f, err := s.client.VirtualMachinesClient.Capture(ctx, resourceGroupName, computeName, *parameters) - if err != nil { - s.say(s.client.LastError.Error()) - } - return f.WaitForCompletionRef(ctx, s.client.VirtualMachinesClient.Client) -} - -func (s *StepCaptureImage) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { - s.say("Capturing image ...") - - var computeName = state.Get(constants.ArmComputeName).(string) - var location = state.Get(constants.ArmLocation).(string) - var resourceGroupName = state.Get(constants.ArmResourceGroupName).(string) - var vmCaptureParameters = state.Get(constants.ArmVirtualMachineCaptureParameters).(*compute.VirtualMachineCaptureParameters) - var imageParameters = state.Get(constants.ArmImageParameters).(*compute.Image) - - var isManagedImage = state.Get(constants.ArmIsManagedImage).(bool) - var targetManagedImageResourceGroupName = state.Get(constants.ArmManagedImageResourceGroupName).(string) - var targetManagedImageName = state.Get(constants.ArmManagedImageName).(string) - var targetManagedImageLocation = state.Get(constants.ArmLocation).(string) - - s.say(fmt.Sprintf(" -> Compute ResourceGroupName : '%s'", resourceGroupName)) - s.say(fmt.Sprintf(" -> Compute Name : '%s'", computeName)) - s.say(fmt.Sprintf(" -> Compute Location : '%s'", location)) - - err := s.generalizeVM(resourceGroupName, computeName) - - if err == nil { - if isManagedImage { - s.say(fmt.Sprintf(" -> Image ResourceGroupName : '%s'", targetManagedImageResourceGroupName)) - s.say(fmt.Sprintf(" -> Image Name : '%s'", targetManagedImageName)) - s.say(fmt.Sprintf(" -> Image Location : '%s'", targetManagedImageLocation)) - err = s.captureManagedImage(ctx, targetManagedImageResourceGroupName, targetManagedImageName, imageParameters) - } else { - err = s.captureVhd(ctx, resourceGroupName, computeName, vmCaptureParameters) - } - } - if err != nil { - state.Put(constants.Error, err) - s.error(err) - - return multistep.ActionHalt - } - - // HACK(chrboum): I do not like this. The capture method should be returning this value - // instead having to pass in another lambda. - // - // Having to resort to capturing the template via an inspector is hack, and once I can - // resolve that I can cleanup this code too. See the comments in azure_client.go for more - // details. - // [paulmey]: autorest.Future now has access to the last http.Response, but I'm not sure if - // the body is still accessible. - template := s.get(s.client) - state.Put(constants.ArmCaptureTemplate, template) - - return multistep.ActionContinue -} - -func (*StepCaptureImage) Cleanup(multistep.StateBag) { -} diff --git a/builder/azure/arm/step_capture_image_test.go b/builder/azure/arm/step_capture_image_test.go deleted file mode 100644 index 3ca6d7756..000000000 --- a/builder/azure/arm/step_capture_image_test.go +++ /dev/null @@ -1,137 +0,0 @@ -package arm - -import ( - "context" - "fmt" - "testing" - - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2018-04-01/compute" - "github.com/hashicorp/packer-plugin-sdk/multistep" - "github.com/hashicorp/packer/builder/azure/common/constants" -) - -func TestStepCaptureImageShouldFailIfCaptureFails(t *testing.T) { - var testSubject = &StepCaptureImage{ - captureVhd: func(context.Context, string, string, *compute.VirtualMachineCaptureParameters) error { - return fmt.Errorf("!! Unit Test FAIL !!") - }, - generalizeVM: func(string, string) error { - return nil - }, - get: func(client *AzureClient) *CaptureTemplate { - return nil - }, - say: func(message string) {}, - error: func(e error) {}, - } - - stateBag := createTestStateBagStepCaptureImage() - - var result = testSubject.Run(context.Background(), stateBag) - if result != multistep.ActionHalt { - t.Fatalf("Expected the step to return 'ActionHalt', but got '%d'.", result) - } - - if _, ok := stateBag.GetOk(constants.Error); ok == false { - t.Fatalf("Expected the step to set stateBag['%s'], but it was not.", constants.Error) - } -} - -func TestStepCaptureImageShouldPassIfCapturePasses(t *testing.T) { - var testSubject = &StepCaptureImage{ - captureVhd: func(context.Context, string, string, *compute.VirtualMachineCaptureParameters) error { return nil }, - generalizeVM: func(string, string) error { - return nil - }, - get: func(client *AzureClient) *CaptureTemplate { - return nil - }, - say: func(message string) {}, - error: func(e error) {}, - } - - stateBag := createTestStateBagStepCaptureImage() - - var result = testSubject.Run(context.Background(), stateBag) - if result != multistep.ActionContinue { - t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result) - } - - if _, ok := stateBag.GetOk(constants.Error); ok == true { - t.Fatalf("Expected the step to not set stateBag['%s'], but it was.", constants.Error) - } -} - -func TestStepCaptureImageShouldTakeStepArgumentsFromStateBag(t *testing.T) { - cancelCh := make(chan<- struct{}) - defer close(cancelCh) - - var actualResourceGroupName string - var actualComputeName string - var actualVirtualMachineCaptureParameters *compute.VirtualMachineCaptureParameters - actualCaptureTemplate := &CaptureTemplate{ - Schema: "!! Unit Test !!", - } - - var testSubject = &StepCaptureImage{ - captureVhd: func(ctx context.Context, resourceGroupName string, computeName string, parameters *compute.VirtualMachineCaptureParameters) error { - actualResourceGroupName = resourceGroupName - actualComputeName = computeName - actualVirtualMachineCaptureParameters = parameters - - return nil - }, - generalizeVM: func(string, string) error { - return nil - }, - get: func(client *AzureClient) *CaptureTemplate { - return actualCaptureTemplate - }, - say: func(message string) {}, - error: func(e error) {}, - } - - stateBag := createTestStateBagStepCaptureImage() - var result = testSubject.Run(context.Background(), stateBag) - - if result != multistep.ActionContinue { - t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result) - } - - var expectedComputeName = stateBag.Get(constants.ArmComputeName).(string) - var expectedResourceGroupName = stateBag.Get(constants.ArmResourceGroupName).(string) - var expectedVirtualMachineCaptureParameters = stateBag.Get(constants.ArmVirtualMachineCaptureParameters).(*compute.VirtualMachineCaptureParameters) - var expectedCaptureTemplate = stateBag.Get(constants.ArmCaptureTemplate).(*CaptureTemplate) - - if actualComputeName != expectedComputeName { - t.Fatal("Expected StepCaptureImage to source 'constants.ArmComputeName' from the state bag, but it did not.") - } - - if actualResourceGroupName != expectedResourceGroupName { - t.Fatal("Expected StepCaptureImage to source 'constants.ArmResourceGroupName' from the state bag, but it did not.") - } - - if actualVirtualMachineCaptureParameters != expectedVirtualMachineCaptureParameters { - t.Fatal("Expected StepCaptureImage to source 'constants.ArmVirtualMachineCaptureParameters' from the state bag, but it did not.") - } - - if actualCaptureTemplate != expectedCaptureTemplate { - t.Fatal("Expected StepCaptureImage to source 'constants.ArmCaptureTemplate' from the state bag, but it did not.") - } -} - -func createTestStateBagStepCaptureImage() multistep.StateBag { - stateBag := new(multistep.BasicStateBag) - - stateBag.Put(constants.ArmLocation, "localhost") - stateBag.Put(constants.ArmComputeName, "Unit Test: ComputeName") - stateBag.Put(constants.ArmResourceGroupName, "Unit Test: ResourceGroupName") - stateBag.Put(constants.ArmVirtualMachineCaptureParameters, &compute.VirtualMachineCaptureParameters{}) - - stateBag.Put(constants.ArmIsManagedImage, false) - stateBag.Put(constants.ArmManagedImageResourceGroupName, "") - stateBag.Put(constants.ArmManagedImageName, "") - stateBag.Put(constants.ArmImageParameters, &compute.Image{}) - - return stateBag -} diff --git a/builder/azure/arm/step_certificate_in_keyvault.go b/builder/azure/arm/step_certificate_in_keyvault.go deleted file mode 100644 index 523d88609..000000000 --- a/builder/azure/arm/step_certificate_in_keyvault.go +++ /dev/null @@ -1,45 +0,0 @@ -package arm - -import ( - "context" - "fmt" - - "github.com/hashicorp/packer-plugin-sdk/multistep" - packersdk "github.com/hashicorp/packer-plugin-sdk/packer" - "github.com/hashicorp/packer/builder/azure/common" - "github.com/hashicorp/packer/builder/azure/common/constants" -) - -type StepCertificateInKeyVault struct { - config *Config - client common.AZVaultClientIface - say func(message string) - error func(e error) -} - -func NewStepCertificateInKeyVault(cli common.AZVaultClientIface, ui packersdk.Ui, config *Config) *StepCertificateInKeyVault { - var step = &StepCertificateInKeyVault{ - client: cli, - config: config, - say: func(message string) { ui.Say(message) }, - error: func(e error) { ui.Error(e.Error()) }, - } - - return step -} - -func (s *StepCertificateInKeyVault) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { - s.say("Setting the certificate in the KeyVault...") - var keyVaultName = state.Get(constants.ArmKeyVaultName).(string) - - err := s.client.SetSecret(keyVaultName, DefaultSecretName, s.config.winrmCertificate) - if err != nil { - s.error(fmt.Errorf("Error setting winrm cert in custom keyvault: %s", err)) - return multistep.ActionHalt - } - - return multistep.ActionContinue -} - -func (*StepCertificateInKeyVault) Cleanup(multistep.StateBag) { -} diff --git a/builder/azure/arm/step_certificate_in_keyvault_test.go b/builder/azure/arm/step_certificate_in_keyvault_test.go deleted file mode 100644 index a892943f3..000000000 --- a/builder/azure/arm/step_certificate_in_keyvault_test.go +++ /dev/null @@ -1,66 +0,0 @@ -package arm - -import ( - "bytes" - "context" - "testing" - - "github.com/hashicorp/packer-plugin-sdk/multistep" - packersdk "github.com/hashicorp/packer-plugin-sdk/packer" - azcommon "github.com/hashicorp/packer/builder/azure/common" - "github.com/hashicorp/packer/builder/azure/common/constants" -) - -func TestNewStepCertificateInKeyVault(t *testing.T) { - cli := azcommon.MockAZVaultClient{} - ui := &packersdk.BasicUi{ - Reader: new(bytes.Buffer), - Writer: new(bytes.Buffer), - } - state := new(multistep.BasicStateBag) - state.Put(constants.ArmKeyVaultName, "testKeyVaultName") - - config := &Config{ - winrmCertificate: "testCertificateString", - } - - certKVStep := NewStepCertificateInKeyVault(&cli, ui, config) - stepAction := certKVStep.Run(context.TODO(), state) - - if stepAction == multistep.ActionHalt { - t.Fatalf("step should have succeeded.") - } - if !cli.SetSecretCalled { - t.Fatalf("Step should have called SetSecret on Azure client.") - } - if cli.SetSecretCert != "testCertificateString" { - t.Fatalf("Step should have read cert from winRMCertificate field on config.") - } - if cli.SetSecretVaultName != "testKeyVaultName" { - t.Fatalf("step should have read keyvault name from state.") - } -} - -func TestNewStepCertificateInKeyVault_error(t *testing.T) { - // Tell mock to return an error - cli := azcommon.MockAZVaultClient{} - cli.IsError = true - - ui := &packersdk.BasicUi{ - Reader: new(bytes.Buffer), - Writer: new(bytes.Buffer), - } - state := new(multistep.BasicStateBag) - state.Put(constants.ArmKeyVaultName, "testKeyVaultName") - - config := &Config{ - winrmCertificate: "testCertificateString", - } - - certKVStep := NewStepCertificateInKeyVault(&cli, ui, config) - stepAction := certKVStep.Run(context.TODO(), state) - - if stepAction != multistep.ActionHalt { - t.Fatalf("step should have failed.") - } -} diff --git a/builder/azure/arm/step_create_resource_group.go b/builder/azure/arm/step_create_resource_group.go deleted file mode 100644 index e8d8c4a75..000000000 --- a/builder/azure/arm/step_create_resource_group.go +++ /dev/null @@ -1,146 +0,0 @@ -package arm - -import ( - "context" - "errors" - "fmt" - - "github.com/Azure/azure-sdk-for-go/services/resources/mgmt/2018-02-01/resources" - "github.com/hashicorp/packer-plugin-sdk/multistep" - packersdk "github.com/hashicorp/packer-plugin-sdk/packer" - "github.com/hashicorp/packer/builder/azure/common/constants" -) - -type StepCreateResourceGroup struct { - client *AzureClient - create func(ctx context.Context, resourceGroupName string, location string, tags map[string]*string) error - say func(message string) - error func(e error) - exists func(ctx context.Context, resourceGroupName string) (bool, error) -} - -func NewStepCreateResourceGroup(client *AzureClient, ui packersdk.Ui) *StepCreateResourceGroup { - var step = &StepCreateResourceGroup{ - client: client, - say: func(message string) { ui.Say(message) }, - error: func(e error) { ui.Error(e.Error()) }, - } - - step.create = step.createResourceGroup - step.exists = step.doesResourceGroupExist - return step -} - -func (s *StepCreateResourceGroup) createResourceGroup(ctx context.Context, resourceGroupName string, location string, tags map[string]*string) error { - _, err := s.client.GroupsClient.CreateOrUpdate(ctx, resourceGroupName, resources.Group{ - Location: &location, - Tags: tags, - }) - - if err != nil { - s.say(s.client.LastError.Error()) - } - return err -} - -func (s *StepCreateResourceGroup) doesResourceGroupExist(ctx context.Context, resourceGroupName string) (bool, error) { - exists, err := s.client.GroupsClient.CheckExistence(ctx, resourceGroupName) - if err != nil { - s.say(s.client.LastError.Error()) - } - - return exists.Response.StatusCode != 404, err -} - -func (s *StepCreateResourceGroup) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { - var doubleResource, ok = state.GetOk(constants.ArmDoubleResourceGroupNameSet) - if ok && doubleResource.(bool) { - err := errors.New("You have filled in both temp_resource_group_name and build_resource_group_name. Please choose one.") - return processStepResult(err, s.error, state) - } - - var resourceGroupName = state.Get(constants.ArmResourceGroupName).(string) - var location = state.Get(constants.ArmLocation).(string) - tags, ok := state.Get(constants.ArmTags).(map[string]*string) - if !ok { - err := fmt.Errorf("failed to extract tags from state bag") - state.Put(constants.Error, err) - s.error(err) - return multistep.ActionHalt - } - - exists, err := s.exists(ctx, resourceGroupName) - if err != nil { - return processStepResult(err, s.error, state) - } - configThinksExists := state.Get(constants.ArmIsExistingResourceGroup).(bool) - if exists != configThinksExists { - if configThinksExists { - err = errors.New("The resource group you want to use does not exist yet. Please use temp_resource_group_name to create a temporary resource group.") - } else { - err = errors.New("A resource group with that name already exists. Please use build_resource_group_name to use an existing resource group.") - } - return processStepResult(err, s.error, state) - } - - // If the resource group exists, we may not have permissions to update it so we don't. - if !exists { - s.say("Creating resource group ...") - - s.say(fmt.Sprintf(" -> ResourceGroupName : '%s'", resourceGroupName)) - s.say(fmt.Sprintf(" -> Location : '%s'", location)) - s.say(fmt.Sprintf(" -> Tags :")) - for k, v := range tags { - s.say(fmt.Sprintf(" ->> %s : %s", k, *v)) - } - err = s.create(ctx, resourceGroupName, location, tags) - if err == nil { - state.Put(constants.ArmIsResourceGroupCreated, true) - } - } else { - s.say("Using existing resource group ...") - s.say(fmt.Sprintf(" -> ResourceGroupName : '%s'", resourceGroupName)) - s.say(fmt.Sprintf(" -> Location : '%s'", location)) - state.Put(constants.ArmIsResourceGroupCreated, true) - } - - return processStepResult(err, s.error, state) -} - -func (s *StepCreateResourceGroup) Cleanup(state multistep.StateBag) { - isCreated, ok := state.GetOk(constants.ArmIsResourceGroupCreated) - if !ok || !isCreated.(bool) { - return - } - - ui := state.Get("ui").(packersdk.Ui) - if state.Get(constants.ArmIsExistingResourceGroup).(bool) { - ui.Say("\nThe resource group was not created by Packer, not deleting ...") - return - } - - ctx := context.TODO() - resourceGroupName := state.Get(constants.ArmResourceGroupName).(string) - if exists, err := s.exists(ctx, resourceGroupName); !exists || err != nil { - return - } - - ui.Say("\nCleanup requested, deleting resource group ...") - f, err := s.client.GroupsClient.Delete(ctx, resourceGroupName) - if err == nil { - if state.Get(constants.ArmAsyncResourceGroupDelete).(bool) { - s.say(fmt.Sprintf("\n Not waiting for Resource Group delete as requested by user. Resource Group Name is %s", resourceGroupName)) - } else { - err = f.WaitForCompletionRef(ctx, s.client.GroupsClient.Client) - } - } - if err != nil { - ui.Error(fmt.Sprintf("Error deleting resource group. Please delete it manually.\n\n"+ - "Name: %s\n"+ - "Error: %s", resourceGroupName, err)) - return - } - if !state.Get(constants.ArmAsyncResourceGroupDelete).(bool) { - ui.Say("Resource group has been deleted.") - } -} diff --git a/builder/azure/arm/step_create_resource_group_test.go b/builder/azure/arm/step_create_resource_group_test.go deleted file mode 100644 index d07d39830..000000000 --- a/builder/azure/arm/step_create_resource_group_test.go +++ /dev/null @@ -1,251 +0,0 @@ -package arm - -import ( - "context" - "errors" - "fmt" - "testing" - - "github.com/hashicorp/packer-plugin-sdk/multistep" - "github.com/hashicorp/packer/builder/azure/common/constants" -) - -func TestStepCreateResourceGroupShouldFailIfBothGroupNames(t *testing.T) { - stateBag := new(multistep.BasicStateBag) - - stateBag.Put(constants.ArmDoubleResourceGroupNameSet, true) - - value := "Unit Test: Tags" - tags := map[string]*string{ - "tag01": &value, - } - - stateBag.Put(constants.ArmTags, tags) - var testSubject = &StepCreateResourceGroup{ - create: func(context.Context, string, string, map[string]*string) error { return nil }, - say: func(message string) {}, - error: func(e error) {}, - exists: func(context.Context, string) (bool, error) { return false, nil }, - } - var result = testSubject.Run(context.Background(), stateBag) - if result != multistep.ActionHalt { - t.Fatalf("Expected the step to return 'ActionHalt', but got '%d'.", result) - } - - if _, ok := stateBag.GetOk(constants.Error); ok == false { - t.Fatalf("Expected the step to set stateBag['%s'], but it was not.", constants.Error) - } -} - -func TestStepCreateResourceGroupShouldFailIfCreateFails(t *testing.T) { - var testSubject = &StepCreateResourceGroup{ - create: func(context.Context, string, string, map[string]*string) error { - return fmt.Errorf("!! Unit Test FAIL !!") - }, - say: func(message string) {}, - error: func(e error) {}, - exists: func(context.Context, string) (bool, error) { return false, nil }, - } - - stateBag := createTestStateBagStepCreateResourceGroup() - - var result = testSubject.Run(context.Background(), stateBag) - if result != multistep.ActionHalt { - t.Fatalf("Expected the step to return 'ActionHalt', but got '%d'.", result) - } - - if _, ok := stateBag.GetOk(constants.Error); ok == false { - t.Fatalf("Expected the step to set stateBag['%s'], but it was not.", constants.Error) - } -} - -func TestStepCreateResourceGroupShouldFailIfExistsFails(t *testing.T) { - var testSubject = &StepCreateResourceGroup{ - create: func(context.Context, string, string, map[string]*string) error { return nil }, - say: func(message string) {}, - error: func(e error) {}, - exists: func(context.Context, string) (bool, error) { return false, errors.New("FAIL") }, - } - - stateBag := createTestStateBagStepCreateResourceGroup() - - var result = testSubject.Run(context.Background(), stateBag) - if result != multistep.ActionHalt { - t.Fatalf("Expected the step to return 'ActionHalt', but got '%d'.", result) - } - - if _, ok := stateBag.GetOk(constants.Error); ok == false { - t.Fatalf("Expected the step to set stateBag['%s'], but it was not.", constants.Error) - } -} - -func TestStepCreateResourceGroupShouldPassIfCreatePasses(t *testing.T) { - var testSubject = &StepCreateResourceGroup{ - create: func(context.Context, string, string, map[string]*string) error { return nil }, - say: func(message string) {}, - error: func(e error) {}, - exists: func(context.Context, string) (bool, error) { return false, nil }, - } - - stateBag := createTestStateBagStepCreateResourceGroup() - - var result = testSubject.Run(context.Background(), stateBag) - if result != multistep.ActionContinue { - t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result) - } - - if _, ok := stateBag.GetOk(constants.Error); ok == true { - t.Fatalf("Expected the step to not set stateBag['%s'], but it was.", constants.Error) - } -} - -func TestStepCreateResourceGroupShouldTakeStepArgumentsFromStateBag(t *testing.T) { - var actualResourceGroupName string - var actualLocation string - var actualTags map[string]*string - - var testSubject = &StepCreateResourceGroup{ - create: func(ctx context.Context, resourceGroupName string, location string, tags map[string]*string) error { - actualResourceGroupName = resourceGroupName - actualLocation = location - actualTags = tags - return nil - }, - say: func(message string) {}, - error: func(e error) {}, - exists: func(context.Context, string) (bool, error) { return false, nil }, - } - - stateBag := createTestStateBagStepCreateResourceGroup() - var result = testSubject.Run(context.Background(), stateBag) - - if result != multistep.ActionContinue { - t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result) - } - - var expectedResourceGroupName = stateBag.Get(constants.ArmResourceGroupName).(string) - var expectedLocation = stateBag.Get(constants.ArmLocation).(string) - var expectedTags = stateBag.Get(constants.ArmTags).(map[string]*string) - - if actualResourceGroupName != expectedResourceGroupName { - t.Fatal("Expected the step to source 'constants.ArmResourceGroupName' from the state bag, but it did not.") - } - - if actualLocation != expectedLocation { - t.Fatal("Expected the step to source 'constants.ArmResourceGroupName' from the state bag, but it did not.") - } - - if len(expectedTags) != len(actualTags) && expectedTags["tag01"] != actualTags["tag01"] { - t.Fatal("Expected the step to source 'constants.ArmTags' from the state bag, but it did not.") - } - - _, ok := stateBag.GetOk(constants.ArmIsResourceGroupCreated) - if !ok { - t.Fatal("Expected the step to add item to stateBag['constants.ArmIsResourceGroupCreated'], but it did not.") - } -} - -func TestStepCreateResourceGroupMarkShouldFailIfTryingExistingButDoesntExist(t *testing.T) { - var testSubject = &StepCreateResourceGroup{ - create: func(context.Context, string, string, map[string]*string) error { - return fmt.Errorf("!! Unit Test FAIL !!") - }, - say: func(message string) {}, - error: func(e error) {}, - exists: func(context.Context, string) (bool, error) { return false, nil }, - } - - stateBag := createTestExistingStateBagStepCreateResourceGroup() - - var result = testSubject.Run(context.Background(), stateBag) - if result != multistep.ActionHalt { - t.Fatalf("Expected the step to return 'ActionHalt', but got '%d'.", result) - } - - if _, ok := stateBag.GetOk(constants.Error); ok == false { - t.Fatalf("Expected the step to set stateBag['%s'], but it was not.", constants.Error) - } -} - -func TestStepCreateResourceGroupMarkShouldFailIfTryingTempButExist(t *testing.T) { - var testSubject = &StepCreateResourceGroup{ - create: func(context.Context, string, string, map[string]*string) error { - return fmt.Errorf("!! Unit Test FAIL !!") - }, - say: func(message string) {}, - error: func(e error) {}, - exists: func(context.Context, string) (bool, error) { return true, nil }, - } - - stateBag := createTestStateBagStepCreateResourceGroup() - - var result = testSubject.Run(context.Background(), stateBag) - if result != multistep.ActionHalt { - t.Fatalf("Expected the step to return 'ActionHalt', but got '%d'.", result) - } - - if _, ok := stateBag.GetOk(constants.Error); ok == false { - t.Fatalf("Expected the step to set stateBag['%s'], but it was not.", constants.Error) - } -} - -func createTestStateBagStepCreateResourceGroup() multistep.StateBag { - stateBag := new(multistep.BasicStateBag) - - stateBag.Put(constants.ArmLocation, "Unit Test: Location") - stateBag.Put(constants.ArmResourceGroupName, "Unit Test: ResourceGroupName") - stateBag.Put(constants.ArmIsExistingResourceGroup, false) - - value := "Unit Test: Tags" - tags := map[string]*string{ - "tag01": &value, - } - - stateBag.Put(constants.ArmTags, tags) - return stateBag -} - -func createTestExistingStateBagStepCreateResourceGroup() multistep.StateBag { - stateBag := new(multistep.BasicStateBag) - - stateBag.Put(constants.ArmLocation, "Unit Test: Location") - stateBag.Put(constants.ArmResourceGroupName, "Unit Test: ResourceGroupName") - stateBag.Put(constants.ArmIsExistingResourceGroup, true) - - value := "Unit Test: Tags" - tags := map[string]*string{ - "tag01": &value, - } - - stateBag.Put(constants.ArmTags, tags) - return stateBag -} - -func TestStepCreateResourceGroupShouldFailIfTagsFailCast(t *testing.T) { - stateBag := new(multistep.BasicStateBag) - - stateBag.Put(constants.ArmLocation, "Unit Test: Location") - stateBag.Put(constants.ArmResourceGroupName, "Unit Test: ResourceGroupName") - stateBag.Put(constants.ArmIsExistingResourceGroup, true) - - value := "Unit Test: Tags" - tags := map[string]string{ - "tag01": value, - } - - stateBag.Put(constants.ArmTags, tags) - var testSubject = &StepCreateResourceGroup{ - create: func(context.Context, string, string, map[string]*string) error { return nil }, - say: func(message string) {}, - error: func(e error) {}, - exists: func(context.Context, string) (bool, error) { return false, nil }, - } - var result = testSubject.Run(context.Background(), stateBag) - if result != multistep.ActionHalt { - t.Fatalf("Expected the step to return 'ActionHalt', but got '%d'.", result) - } - - if _, ok := stateBag.GetOk(constants.Error); ok == false { - t.Fatalf("Expected the step to set stateBag['%s'], but it was not.", constants.Error) - } -} diff --git a/builder/azure/arm/step_delete_additional_disks.go b/builder/azure/arm/step_delete_additional_disks.go deleted file mode 100644 index f87992323..000000000 --- a/builder/azure/arm/step_delete_additional_disks.go +++ /dev/null @@ -1,118 +0,0 @@ -package arm - -import ( - "context" - "errors" - "fmt" - "net/url" - "strings" - - "github.com/hashicorp/packer/builder/azure/common/constants" - - "github.com/hashicorp/packer-plugin-sdk/multistep" - packersdk "github.com/hashicorp/packer-plugin-sdk/packer" -) - -type StepDeleteAdditionalDisk struct { - client *AzureClient - delete func(string, string) error - deleteManaged func(context.Context, string, string) error - say func(message string) - error func(e error) -} - -func NewStepDeleteAdditionalDisks(client *AzureClient, ui packersdk.Ui) *StepDeleteAdditionalDisk { - var step = &StepDeleteAdditionalDisk{ - client: client, - say: func(message string) { ui.Say(message) }, - error: func(e error) { ui.Error(e.Error()) }, - } - - step.delete = step.deleteBlob - step.deleteManaged = step.deleteManagedDisk - return step -} - -func (s *StepDeleteAdditionalDisk) deleteBlob(storageContainerName string, blobName string) error { - blob := s.client.BlobStorageClient.GetContainerReference(storageContainerName).GetBlobReference(blobName) - _, err := blob.BreakLease(nil) - if err != nil && !strings.Contains(err.Error(), "LeaseNotPresentWithLeaseOperation") { - s.say(s.client.LastError.Error()) - return err - } - - err = blob.Delete(nil) - if err != nil { - s.say(s.client.LastError.Error()) - } - return err -} - -func (s *StepDeleteAdditionalDisk) deleteManagedDisk(ctx context.Context, resourceGroupName string, diskName string) error { - xs := strings.Split(diskName, "/") - diskName = xs[len(xs)-1] - f, err := s.client.DisksClient.Delete(ctx, resourceGroupName, diskName) - if err == nil { - err = f.WaitForCompletionRef(ctx, s.client.DisksClient.Client) - } - return err -} - -func (s *StepDeleteAdditionalDisk) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { - s.say("Deleting the temporary Additional disk ...") - - var dataDisks []string - - if disks := state.Get(constants.ArmAdditionalDiskVhds); disks != nil { - dataDisks = disks.([]string) - } - var isManagedDisk = state.Get(constants.ArmIsManagedImage).(bool) - var isExistingResourceGroup = state.Get(constants.ArmIsExistingResourceGroup).(bool) - var resourceGroupName = state.Get(constants.ArmResourceGroupName).(string) - - if dataDisks == nil { - s.say(fmt.Sprintf(" -> No Additional Disks specified")) - return multistep.ActionContinue - } - - if isManagedDisk && !isExistingResourceGroup { - s.say(fmt.Sprintf(" -> Additional Disk : skipping, managed disk was used...")) - return multistep.ActionContinue - } - - for i, additionaldisk := range dataDisks { - s.say(fmt.Sprintf(" -> Additional Disk %d: '%s'", i+1, additionaldisk)) - var err error - if isManagedDisk { - err = s.deleteManaged(ctx, resourceGroupName, additionaldisk) - if err != nil { - s.say("Failed to delete the managed Additional Disk!") - return processStepResult(err, s.error, state) - } - continue - } - - u, err := url.Parse(additionaldisk) - if err != nil { - s.say("Failed to parse the Additional Disk's VHD URI!") - return processStepResult(err, s.error, state) - } - - xs := strings.Split(u.Path, "/") - if len(xs) < 3 { - err = errors.New("Failed to parse Additional Disk's VHD URI!") - } else { - var storageAccountName = xs[1] - var blobName = strings.Join(xs[2:], "/") - - err = s.delete(storageAccountName, blobName) - } - if err != nil { - return processStepResult(err, s.error, state) - } - } - return multistep.ActionContinue -} - -func (*StepDeleteAdditionalDisk) Cleanup(multistep.StateBag) { -} diff --git a/builder/azure/arm/step_delete_additional_disks_test.go b/builder/azure/arm/step_delete_additional_disks_test.go deleted file mode 100644 index 1a2729012..000000000 --- a/builder/azure/arm/step_delete_additional_disks_test.go +++ /dev/null @@ -1,227 +0,0 @@ -package arm - -import ( - "context" - "errors" - "fmt" - "testing" - - "github.com/hashicorp/packer-plugin-sdk/multistep" - "github.com/hashicorp/packer/builder/azure/common/constants" -) - -func TestStepDeleteAdditionalDiskShouldFailIfGetFails(t *testing.T) { - var testSubject = &StepDeleteAdditionalDisk{ - delete: func(string, string) error { return fmt.Errorf("!! Unit Test FAIL !!") }, - deleteManaged: func(context.Context, string, string) error { return nil }, - say: func(message string) {}, - error: func(e error) {}, - } - - stateBag := DeleteTestStateBagStepDeleteAdditionalDisk([]string{"http://storage.blob.core.windows.net/images/pkrvm_os.vhd"}) - - var result = testSubject.Run(context.Background(), stateBag) - if result != multistep.ActionHalt { - t.Fatalf("Expected the step to return 'ActionHalt', but got '%d'.", result) - } - - if _, ok := stateBag.GetOk(constants.Error); ok == false { - t.Fatalf("Expected the step to set stateBag['%s'], but it was not.", constants.Error) - } -} - -func TestStepDeleteAdditionalDiskShouldPassIfGetPasses(t *testing.T) { - var testSubject = &StepDeleteAdditionalDisk{ - delete: func(string, string) error { return nil }, - say: func(message string) {}, - error: func(e error) {}, - } - - stateBag := DeleteTestStateBagStepDeleteAdditionalDisk([]string{"http://storage.blob.core.windows.net/images/pkrvm_os.vhd"}) - - var result = testSubject.Run(context.Background(), stateBag) - if result != multistep.ActionContinue { - t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result) - } - - if _, ok := stateBag.GetOk(constants.Error); ok == true { - t.Fatalf("Expected the step to not set stateBag['%s'], but it was.", constants.Error) - } -} - -func TestStepDeleteAdditionalDiskShouldTakeStepArgumentsFromStateBag(t *testing.T) { - var actualStorageContainerName string - var actualBlobName string - - var testSubject = &StepDeleteAdditionalDisk{ - delete: func(storageContainerName string, blobName string) error { - actualStorageContainerName = storageContainerName - actualBlobName = blobName - return nil - }, - say: func(message string) {}, - error: func(e error) {}, - } - - stateBag := DeleteTestStateBagStepDeleteAdditionalDisk([]string{"http://storage.blob.core.windows.net/images/pkrvm_os.vhd"}) - var result = testSubject.Run(context.Background(), stateBag) - - if result != multistep.ActionContinue { - t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result) - } - - if actualStorageContainerName != "images" { - t.Fatalf("Expected the storage container name to be 'images', but found '%s'.", actualStorageContainerName) - } - - if actualBlobName != "pkrvm_os.vhd" { - t.Fatalf("Expected the blob name to be 'pkrvm_os.vhd', but found '%s'.", actualBlobName) - } -} - -func TestStepDeleteAdditionalDiskShouldHandleComplexStorageContainerNames(t *testing.T) { - var actualStorageContainerName string - var actualBlobName string - - var testSubject = &StepDeleteAdditionalDisk{ - delete: func(storageContainerName string, blobName string) error { - actualStorageContainerName = storageContainerName - actualBlobName = blobName - return nil - }, - say: func(message string) {}, - error: func(e error) {}, - } - - stateBag := DeleteTestStateBagStepDeleteAdditionalDisk([]string{"http://storage.blob.core.windows.net/abc/def/pkrvm_os.vhd"}) - testSubject.Run(context.Background(), stateBag) - - if actualStorageContainerName != "abc" { - t.Fatalf("Expected the storage container name to be 'abc/def', but found '%s'.", actualStorageContainerName) - } - - if actualBlobName != "def/pkrvm_os.vhd" { - t.Fatalf("Expected the blob name to be 'pkrvm_os.vhd', but found '%s'.", actualBlobName) - } -} - -func TestStepDeleteAdditionalDiskShouldFailIfVHDNameCannotBeURLParsed(t *testing.T) { - var testSubject = &StepDeleteAdditionalDisk{ - delete: func(string, string) error { return nil }, - say: func(message string) {}, - error: func(e error) {}, - deleteManaged: func(context.Context, string, string) error { return nil }, - } - - // Invalid URL per https://golang.org/src/net/url/url_test.go - stateBag := DeleteTestStateBagStepDeleteAdditionalDisk([]string{"http://[fe80::1%en0]/"}) - - var result = testSubject.Run(context.Background(), stateBag) - if result != multistep.ActionHalt { - t.Fatalf("Expected the step to return 'ActionHalt', but got '%v'.", result) - } - - if _, ok := stateBag.GetOk(constants.Error); ok == false { - t.Fatalf("Expected the step to not stateBag['%s'], but it was.", constants.Error) - } -} -func TestStepDeleteAdditionalDiskShouldFailIfVHDNameIsTooShort(t *testing.T) { - var testSubject = &StepDeleteAdditionalDisk{ - delete: func(string, string) error { return nil }, - say: func(message string) {}, - error: func(e error) {}, - deleteManaged: func(context.Context, string, string) error { return nil }, - } - - stateBag := DeleteTestStateBagStepDeleteAdditionalDisk([]string{"storage.blob.core.windows.net/abc"}) - - var result = testSubject.Run(context.Background(), stateBag) - if result != multistep.ActionHalt { - t.Fatalf("Expected the step to return 'ActionHalt', but got '%d'.", result) - } - - if _, ok := stateBag.GetOk(constants.Error); ok == false { - t.Fatalf("Expected the step to not stateBag['%s'], but it was.", constants.Error) - } -} - -func TestStepDeleteAdditionalDiskShouldPassIfManagedDiskInTempResourceGroup(t *testing.T) { - var testSubject = &StepDeleteAdditionalDisk{ - delete: func(string, string) error { return nil }, - say: func(message string) {}, - error: func(e error) {}, - } - - stateBag := new(multistep.BasicStateBag) - stateBag.Put(constants.ArmAdditionalDiskVhds, []string{"subscriptions/123-456-789/resourceGroups/existingresourcegroup/providers/Microsoft.Compute/disks/osdisk"}) - stateBag.Put(constants.ArmIsManagedImage, true) - stateBag.Put(constants.ArmIsExistingResourceGroup, false) - stateBag.Put(constants.ArmResourceGroupName, "testgroup") - - var result = testSubject.Run(context.Background(), stateBag) - if result != multistep.ActionContinue { - t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result) - } - - if _, ok := stateBag.GetOk(constants.Error); ok == true { - t.Fatalf("Expected the step to not set stateBag['%s'], but it was.", constants.Error) - } -} - -func TestStepDeleteAdditionalDiskShouldFailIfManagedDiskInExistingResourceGroupFailsToDelete(t *testing.T) { - var testSubject = &StepDeleteAdditionalDisk{ - delete: func(string, string) error { return nil }, - say: func(message string) {}, - error: func(e error) {}, - deleteManaged: func(context.Context, string, string) error { return errors.New("UNIT TEST FAIL!") }, - } - - stateBag := new(multistep.BasicStateBag) - stateBag.Put(constants.ArmAdditionalDiskVhds, []string{"subscriptions/123-456-789/resourceGroups/existingresourcegroup/providers/Microsoft.Compute/disks/osdisk"}) - stateBag.Put(constants.ArmIsManagedImage, true) - stateBag.Put(constants.ArmIsExistingResourceGroup, true) - stateBag.Put(constants.ArmResourceGroupName, "testgroup") - - var result = testSubject.Run(context.Background(), stateBag) - if result != multistep.ActionHalt { - t.Fatalf("Expected the step to return 'ActionHalt', but got '%d'.", result) - } - - if _, ok := stateBag.GetOk(constants.Error); ok == false { - t.Fatalf("Expected the step to not stateBag['%s'], but it was.", constants.Error) - } -} - -func TestStepDeleteAdditionalDiskShouldFailIfManagedDiskInExistingResourceGroupIsDeleted(t *testing.T) { - var testSubject = &StepDeleteAdditionalDisk{ - delete: func(string, string) error { return nil }, - say: func(message string) {}, - error: func(e error) {}, - deleteManaged: func(context.Context, string, string) error { return nil }, - } - - stateBag := new(multistep.BasicStateBag) - stateBag.Put(constants.ArmAdditionalDiskVhds, []string{"subscriptions/123-456-789/resourceGroups/existingresourcegroup/providers/Microsoft.Compute/disks/osdisk"}) - stateBag.Put(constants.ArmIsManagedImage, true) - stateBag.Put(constants.ArmIsExistingResourceGroup, true) - stateBag.Put(constants.ArmResourceGroupName, "testgroup") - - var result = testSubject.Run(context.Background(), stateBag) - if result != multistep.ActionContinue { - t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result) - } - - if _, ok := stateBag.GetOk(constants.Error); ok == true { - t.Fatalf("Expected the step to not set stateBag['%s'], but it was.", constants.Error) - } -} - -func DeleteTestStateBagStepDeleteAdditionalDisk(osDiskVhds []string) multistep.StateBag { - stateBag := new(multistep.BasicStateBag) - stateBag.Put(constants.ArmAdditionalDiskVhds, osDiskVhds) - stateBag.Put(constants.ArmIsManagedImage, false) - stateBag.Put(constants.ArmIsExistingResourceGroup, false) - stateBag.Put(constants.ArmResourceGroupName, "testgroup") - - return stateBag -} diff --git a/builder/azure/arm/step_deploy_template.go b/builder/azure/arm/step_deploy_template.go deleted file mode 100644 index 9c40c732f..000000000 --- a/builder/azure/arm/step_deploy_template.go +++ /dev/null @@ -1,306 +0,0 @@ -package arm - -import ( - "context" - "errors" - "fmt" - "net/url" - "strings" - "sync" - "time" - - "github.com/hashicorp/packer-plugin-sdk/multistep" - packersdk "github.com/hashicorp/packer-plugin-sdk/packer" - "github.com/hashicorp/packer-plugin-sdk/retry" - "github.com/hashicorp/packer/builder/azure/common/constants" -) - -type StepDeployTemplate struct { - client *AzureClient - deploy func(ctx context.Context, resourceGroupName string, deploymentName string) error - delete func(ctx context.Context, deploymentName, resourceGroupName string) error - disk func(ctx context.Context, resourceGroupName string, computeName string) (string, string, error) - deleteDisk func(ctx context.Context, imageType string, imageName string, resourceGroupName string) error - deleteDeployment func(ctx context.Context, state multistep.StateBag) error - say func(message string) - error func(e error) - config *Config - factory templateFactoryFunc - name string -} - -func NewStepDeployTemplate(client *AzureClient, ui packersdk.Ui, config *Config, deploymentName string, factory templateFactoryFunc) *StepDeployTemplate { - var step = &StepDeployTemplate{ - client: client, - say: func(message string) { ui.Say(message) }, - error: func(e error) { ui.Error(e.Error()) }, - config: config, - factory: factory, - name: deploymentName, - } - - step.deploy = step.deployTemplate - step.delete = step.deleteDeploymentResources - step.disk = step.getImageDetails - step.deleteDisk = step.deleteImage - step.deleteDeployment = step.deleteDeploymentObject - return step -} - -func (s *StepDeployTemplate) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { - s.say("Deploying deployment template ...") - - var resourceGroupName = state.Get(constants.ArmResourceGroupName).(string) - s.say(fmt.Sprintf(" -> ResourceGroupName : '%s'", resourceGroupName)) - s.say(fmt.Sprintf(" -> DeploymentName : '%s'", s.name)) - - return processStepResult( - s.deploy(ctx, resourceGroupName, s.name), - s.error, state) -} - -func (s *StepDeployTemplate) Cleanup(state multistep.StateBag) { - defer func() { - err := s.deleteDeployment(context.Background(), state) - if err != nil { - s.say(err.Error()) - } - }() - - ui := state.Get("ui").(packersdk.Ui) - ui.Say("\nDeleting individual resources ...") - - deploymentName := s.name - resourceGroupName := state.Get(constants.ArmResourceGroupName).(string) - // Get image disk details before deleting the image; otherwise we won't be able to - // delete the disk as the image request will return a 404 - computeName := state.Get(constants.ArmComputeName).(string) - imageType, imageName, err := s.disk(context.TODO(), resourceGroupName, computeName) - - if err != nil && !strings.Contains(err.Error(), "ResourceNotFound") { - ui.Error(fmt.Sprintf("Could not retrieve OS Image details: %s", err)) - } - err = s.delete(context.TODO(), deploymentName, resourceGroupName) - if err != nil { - s.reportIfError(err, resourceGroupName) - } - - // The disk was not found on the VM, this is an error. - if imageType == "" && imageName == "" { - ui.Error(fmt.Sprintf("Failed to find temporary OS disk on VM. Please delete manually.\n\n"+ - "VM Name: %s\n"+ - "Error: %s", computeName, err)) - return - } - if !state.Get(constants.ArmKeepOSDisk).(bool) { - ui.Say(fmt.Sprintf(" Deleting -> %s : '%s'", imageType, imageName)) - err = s.deleteDisk(context.TODO(), imageType, imageName, resourceGroupName) - if err != nil { - ui.Error(fmt.Sprintf("Error deleting resource. Please delete manually.\n\n"+ - "Name: %s\n"+ - "Error: %s", imageName, err)) - } - } -} - -func (s *StepDeployTemplate) deployTemplate(ctx context.Context, resourceGroupName string, deploymentName string) error { - deployment, err := s.factory(s.config) - if err != nil { - return err - } - - f, err := s.client.DeploymentsClient.CreateOrUpdate(ctx, resourceGroupName, deploymentName, *deployment) - if err != nil { - s.say(s.client.LastError.Error()) - return err - } - - err = f.WaitForCompletionRef(ctx, s.client.DeploymentsClient.Client) - if err == nil { - s.say(s.client.LastError.Error()) - } - - return err -} - -func (s *StepDeployTemplate) deleteDeploymentObject(ctx context.Context, state multistep.StateBag) error { - deploymentName := s.name - resourceGroupName := state.Get(constants.ArmResourceGroupName).(string) - ui := state.Get("ui").(packersdk.Ui) - - ui.Say(fmt.Sprintf("Removing the created Deployment object: '%s'", deploymentName)) - f, err := s.client.DeploymentsClient.Delete(ctx, resourceGroupName, deploymentName) - if err != nil { - return err - } - - return f.WaitForCompletionRef(ctx, s.client.DeploymentsClient.Client) -} - -func (s *StepDeployTemplate) getImageDetails(ctx context.Context, resourceGroupName string, computeName string) (string, string, error) { - //We can't depend on constants.ArmOSDiskVhd being set - var imageName, imageType string - vm, err := s.client.VirtualMachinesClient.Get(ctx, resourceGroupName, computeName, "") - if err != nil { - return imageName, imageType, err - } - - if vm.StorageProfile.OsDisk.Vhd != nil { - imageType = "image" - imageName = *vm.StorageProfile.OsDisk.Vhd.URI - return imageType, imageName, nil - } - - imageType = "Microsoft.Compute/disks" - imageName = *vm.StorageProfile.OsDisk.ManagedDisk.ID - - return imageType, imageName, nil -} - -//TODO(paulmey): move to helpers file -func deleteResource(ctx context.Context, client *AzureClient, resourceType string, resourceName string, resourceGroupName string) error { - switch resourceType { - case "Microsoft.Compute/virtualMachines": - f, err := client.VirtualMachinesClient.Delete(ctx, resourceGroupName, resourceName) - if err == nil { - err = f.WaitForCompletionRef(ctx, client.VirtualMachinesClient.Client) - } - return err - case "Microsoft.KeyVault/vaults": - _, err := client.VaultClientDelete.Delete(ctx, resourceGroupName, resourceName) - return err - case "Microsoft.Network/networkInterfaces": - f, err := client.InterfacesClient.Delete(ctx, resourceGroupName, resourceName) - if err == nil { - err = f.WaitForCompletionRef(ctx, client.InterfacesClient.Client) - } - return err - case "Microsoft.Network/virtualNetworks": - f, err := client.VirtualNetworksClient.Delete(ctx, resourceGroupName, resourceName) - if err == nil { - err = f.WaitForCompletionRef(ctx, client.VirtualNetworksClient.Client) - } - return err - case "Microsoft.Network/networkSecurityGroups": - f, err := client.SecurityGroupsClient.Delete(ctx, resourceGroupName, resourceName) - if err == nil { - err = f.WaitForCompletionRef(ctx, client.SecurityGroupsClient.Client) - } - return err - case "Microsoft.Network/publicIPAddresses": - f, err := client.PublicIPAddressesClient.Delete(ctx, resourceGroupName, resourceName) - if err == nil { - err = f.WaitForCompletionRef(ctx, client.PublicIPAddressesClient.Client) - } - return err - } - return nil -} - -func (s *StepDeployTemplate) deleteImage(ctx context.Context, imageType string, imageName string, resourceGroupName string) error { - // Managed disk - if imageType == "Microsoft.Compute/disks" { - xs := strings.Split(imageName, "/") - diskName := xs[len(xs)-1] - f, err := s.client.DisksClient.Delete(ctx, resourceGroupName, diskName) - if err == nil { - err = f.WaitForCompletionRef(ctx, s.client.DisksClient.Client) - } - return err - } - - // VHD image - u, err := url.Parse(imageName) - if err != nil { - return err - } - xs := strings.Split(u.Path, "/") - if len(xs) < 3 { - return errors.New("Unable to parse path of image " + imageName) - } - var storageAccountName = xs[1] - var blobName = strings.Join(xs[2:], "/") - - blob := s.client.BlobStorageClient.GetContainerReference(storageAccountName).GetBlobReference(blobName) - _, err = blob.BreakLease(nil) - if err != nil && !strings.Contains(err.Error(), "LeaseNotPresentWithLeaseOperation") { - s.say(s.client.LastError.Error()) - return err - } - - return blob.Delete(nil) -} - -func (s *StepDeployTemplate) deleteDeploymentResources(ctx context.Context, deploymentName, resourceGroupName string) error { - var maxResources int32 = 50 - deploymentOperations, err := s.client.DeploymentOperationsClient.ListComplete(ctx, resourceGroupName, deploymentName, &maxResources) - if err != nil { - s.reportIfError(err, resourceGroupName) - return err - } - - resources := map[string]string{} - - for deploymentOperations.NotDone() { - deploymentOperation := deploymentOperations.Value() - // Sometimes an empty operation is added to the list by Azure - if deploymentOperation.Properties.TargetResource == nil { - _ = deploymentOperations.Next() - continue - } - - resourceName := *deploymentOperation.Properties.TargetResource.ResourceName - resourceType := *deploymentOperation.Properties.TargetResource.ResourceType - - s.say(fmt.Sprintf("Adding to deletion queue -> %s : '%s'", resourceType, resourceName)) - resources[resourceType] = resourceName - - if err = deploymentOperations.Next(); err != nil { - return err - } - } - - var wg sync.WaitGroup - wg.Add(len(resources)) - - for resourceType, resourceName := range resources { - go func(resourceType, resourceName string) { - defer wg.Done() - retryConfig := retry.Config{ - Tries: 10, - RetryDelay: (&retry.Backoff{InitialBackoff: 10 * time.Second, MaxBackoff: 600 * time.Second, Multiplier: 2}).Linear, - } - - err = retryConfig.Run(ctx, func(ctx context.Context) error { - s.say(fmt.Sprintf("Attempting deletion -> %s : '%s'", resourceType, resourceName)) - err := deleteResource(ctx, s.client, - resourceType, - resourceName, - resourceGroupName) - if err != nil { - s.say(fmt.Sprintf("Error deleting resource. Will retry.\n"+ - "Name: %s\n"+ - "Error: %s\n", resourceName, err.Error())) - } - return err - }) - if err != nil { - s.reportIfError(err, resourceName) - } - }(resourceType, resourceName) - } - - s.say("Waiting for deletion of all resources...") - wg.Wait() - - return nil -} - -func (s *StepDeployTemplate) reportIfError(err error, resourceName string) { - if err != nil { - s.say(fmt.Sprintf("Error deleting resource. Please delete manually.\n\n"+ - "Name: %s\n"+ - "Error: %s", resourceName, err.Error())) - s.error(err) - } -} diff --git a/builder/azure/arm/step_deploy_template_test.go b/builder/azure/arm/step_deploy_template_test.go deleted file mode 100644 index 088b9901a..000000000 --- a/builder/azure/arm/step_deploy_template_test.go +++ /dev/null @@ -1,209 +0,0 @@ -package arm - -import ( - "context" - "fmt" - "testing" - - "github.com/hashicorp/packer-plugin-sdk/multistep" - packersdk "github.com/hashicorp/packer-plugin-sdk/packer" - "github.com/hashicorp/packer/builder/azure/common/constants" -) - -func TestStepDeployTemplateShouldFailIfDeployFails(t *testing.T) { - var testSubject = &StepDeployTemplate{ - deploy: func(context.Context, string, string) error { - return fmt.Errorf("!! Unit Test FAIL !!") - }, - say: func(message string) {}, - error: func(e error) {}, - } - - stateBag := createTestStateBagStepDeployTemplate() - - var result = testSubject.Run(context.Background(), stateBag) - if result != multistep.ActionHalt { - t.Fatalf("Expected the step to return 'ActionHalt', but got '%d'.", result) - } - - if _, ok := stateBag.GetOk(constants.Error); ok == false { - t.Fatalf("Expected the step to set stateBag['%s'], but it was not.", constants.Error) - } -} - -func TestStepDeployTemplateShouldPassIfDeployPasses(t *testing.T) { - var testSubject = &StepDeployTemplate{ - deploy: func(context.Context, string, string) error { return nil }, - say: func(message string) {}, - error: func(e error) {}, - } - - stateBag := createTestStateBagStepDeployTemplate() - - var result = testSubject.Run(context.Background(), stateBag) - if result != multistep.ActionContinue { - t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result) - } - - if _, ok := stateBag.GetOk(constants.Error); ok == true { - t.Fatalf("Expected the step to not set stateBag['%s'], but it was.", constants.Error) - } -} - -func TestStepDeployTemplateShouldTakeStepArgumentsFromStateBag(t *testing.T) { - var actualResourceGroupName string - var actualDeploymentName string - - var testSubject = &StepDeployTemplate{ - deploy: func(ctx context.Context, resourceGroupName string, deploymentName string) error { - actualResourceGroupName = resourceGroupName - actualDeploymentName = deploymentName - - return nil - }, - say: func(message string) {}, - error: func(e error) {}, - name: "--deployment-name--", - } - - stateBag := createTestStateBagStepValidateTemplate() - var result = testSubject.Run(context.Background(), stateBag) - - if result != multistep.ActionContinue { - t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result) - } - - var expectedResourceGroupName = stateBag.Get(constants.ArmResourceGroupName).(string) - - if actualDeploymentName != "--deployment-name--" { - t.Fatal("Expected StepValidateTemplate to source 'constants.ArmDeploymentName' from the state bag, but it did not.") - } - - if actualResourceGroupName != expectedResourceGroupName { - t.Fatal("Expected the step to source 'constants.ArmResourceGroupName' from the state bag, but it did not.") - } -} - -func TestStepDeployTemplateDeleteImageShouldFailWhenImageUrlCannotBeParsed(t *testing.T) { - var testSubject = &StepDeployTemplate{ - say: func(message string) {}, - error: func(e error) {}, - name: "--deployment-name--", - } - // Invalid URL per https://golang.org/src/net/url/url_test.go - err := testSubject.deleteImage(context.TODO(), "image", "http://[fe80::1%en0]/", "Unit Test: ResourceGroupName") - if err == nil { - t.Fatal("Expected a failure because of the failed image name") - } -} - -func TestStepDeployTemplateDeleteImageShouldFailWithInvalidImage(t *testing.T) { - var testSubject = &StepDeployTemplate{ - say: func(message string) {}, - error: func(e error) {}, - name: "--deployment-name--", - } - err := testSubject.deleteImage(context.TODO(), "image", "storage.blob.core.windows.net/abc", "Unit Test: ResourceGroupName") - if err == nil { - t.Fatal("Expected a failure because of the failed image name") - } -} - -func TestStepDeployTemplateCleanupShouldDeleteManagedOSImageInExistingResourceGroup(t *testing.T) { - var deleteDiskCounter = 0 - var testSubject = createTestStepDeployTemplateDeleteOSImage(&deleteDiskCounter) - - stateBag := createTestStateBagStepDeployTemplate() - stateBag.Put(constants.ArmIsManagedImage, true) - stateBag.Put(constants.ArmIsExistingResourceGroup, true) - stateBag.Put(constants.ArmIsResourceGroupCreated, true) - stateBag.Put(constants.ArmKeepOSDisk, false) - stateBag.Put("ui", packersdk.TestUi(t)) - - testSubject.Cleanup(stateBag) - if deleteDiskCounter != 1 { - t.Fatalf("Expected DeployTemplate Cleanup to invoke deleteDisk 1 time, but invoked %d times", deleteDiskCounter) - } -} - -func TestStepDeployTemplateCleanupShouldDeleteManagedOSImageInTemporaryResourceGroup(t *testing.T) { - var deleteDiskCounter = 0 - var testSubject = createTestStepDeployTemplateDeleteOSImage(&deleteDiskCounter) - - stateBag := createTestStateBagStepDeployTemplate() - stateBag.Put(constants.ArmIsManagedImage, true) - stateBag.Put(constants.ArmIsExistingResourceGroup, false) - stateBag.Put(constants.ArmIsResourceGroupCreated, true) - stateBag.Put(constants.ArmKeepOSDisk, false) - stateBag.Put("ui", packersdk.TestUi(t)) - - testSubject.Cleanup(stateBag) - if deleteDiskCounter != 1 { - t.Fatalf("Expected DeployTemplate Cleanup to invoke deleteDisk 1 times, but invoked %d times", deleteDiskCounter) - } -} - -func TestStepDeployTemplateCleanupShouldDeleteVHDOSImageInExistingResourceGroup(t *testing.T) { - var deleteDiskCounter = 0 - var testSubject = createTestStepDeployTemplateDeleteOSImage(&deleteDiskCounter) - - stateBag := createTestStateBagStepDeployTemplate() - stateBag.Put(constants.ArmIsManagedImage, false) - stateBag.Put(constants.ArmIsExistingResourceGroup, true) - stateBag.Put(constants.ArmIsResourceGroupCreated, true) - stateBag.Put(constants.ArmKeepOSDisk, false) - stateBag.Put("ui", packersdk.TestUi(t)) - - testSubject.Cleanup(stateBag) - if deleteDiskCounter != 1 { - t.Fatalf("Expected DeployTemplate Cleanup to invoke deleteDisk 1 time, but invoked %d times", deleteDiskCounter) - } -} - -func TestStepDeployTemplateCleanupShouldVHDOSImageInTemporaryResourceGroup(t *testing.T) { - var deleteDiskCounter = 0 - var testSubject = createTestStepDeployTemplateDeleteOSImage(&deleteDiskCounter) - - stateBag := createTestStateBagStepDeployTemplate() - stateBag.Put(constants.ArmIsManagedImage, false) - stateBag.Put(constants.ArmIsExistingResourceGroup, false) - stateBag.Put(constants.ArmIsResourceGroupCreated, true) - stateBag.Put(constants.ArmKeepOSDisk, false) - stateBag.Put("ui", packersdk.TestUi(t)) - - testSubject.Cleanup(stateBag) - if deleteDiskCounter != 1 { - t.Fatalf("Expected DeployTemplate Cleanup to invoke deleteDisk 1 times, but invoked %d times", deleteDiskCounter) - } -} - -func createTestStateBagStepDeployTemplate() multistep.StateBag { - stateBag := new(multistep.BasicStateBag) - - stateBag.Put(constants.ArmDeploymentName, "Unit Test: DeploymentName") - stateBag.Put(constants.ArmResourceGroupName, "Unit Test: ResourceGroupName") - stateBag.Put(constants.ArmComputeName, "Unit Test: ComputeName") - - return stateBag -} - -func createTestStepDeployTemplateDeleteOSImage(deleteDiskCounter *int) *StepDeployTemplate { - return &StepDeployTemplate{ - deploy: func(context.Context, string, string) error { return nil }, - say: func(message string) {}, - error: func(e error) {}, - deleteDisk: func(ctx context.Context, imageType string, imageName string, resourceGroupName string) error { - *deleteDiskCounter++ - return nil - }, - disk: func(ctx context.Context, resourceGroupName, computeName string) (string, string, error) { - return "Microsoft.Compute/disks", "", nil - }, - delete: func(ctx context.Context, deploymentName, resourceGroupName string) error { - return nil - }, - deleteDeployment: func(ctx context.Context, state multistep.StateBag) error { - return nil - }, - } -} diff --git a/builder/azure/arm/step_get_additional_disks.go b/builder/azure/arm/step_get_additional_disks.go deleted file mode 100644 index bccc6faa0..000000000 --- a/builder/azure/arm/step_get_additional_disks.go +++ /dev/null @@ -1,78 +0,0 @@ -package arm - -import ( - "context" - "fmt" - - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2018-04-01/compute" - - "github.com/hashicorp/packer/builder/azure/common/constants" - - "github.com/hashicorp/packer-plugin-sdk/multistep" - packersdk "github.com/hashicorp/packer-plugin-sdk/packer" -) - -type StepGetDataDisk struct { - client *AzureClient - query func(ctx context.Context, resourceGroupName string, computeName string) (compute.VirtualMachine, error) - say func(message string) - error func(e error) -} - -func NewStepGetAdditionalDisks(client *AzureClient, ui packersdk.Ui) *StepGetDataDisk { - var step = &StepGetDataDisk{ - client: client, - say: func(message string) { ui.Say(message) }, - error: func(e error) { ui.Error(e.Error()) }, - } - - step.query = step.queryCompute - return step -} - -func (s *StepGetDataDisk) queryCompute(ctx context.Context, resourceGroupName string, computeName string) (compute.VirtualMachine, error) { - vm, err := s.client.VirtualMachinesClient.Get(ctx, resourceGroupName, computeName, "") - if err != nil { - s.say(s.client.LastError.Error()) - } - return vm, err -} - -func (s *StepGetDataDisk) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { - s.say("Querying the machine's additional disks properties ...") - - var resourceGroupName = state.Get(constants.ArmResourceGroupName).(string) - var computeName = state.Get(constants.ArmComputeName).(string) - - s.say(fmt.Sprintf(" -> ResourceGroupName : '%s'", resourceGroupName)) - s.say(fmt.Sprintf(" -> ComputeName : '%s'", computeName)) - - vm, err := s.query(ctx, resourceGroupName, computeName) - if err != nil { - state.Put(constants.Error, err) - s.error(err) - - return multistep.ActionHalt - } - - if vm.StorageProfile.DataDisks != nil { - var vhdUri string - additional_disks := make([]string, len(*vm.StorageProfile.DataDisks)) - for i, additionaldisk := range *vm.StorageProfile.DataDisks { - if additionaldisk.Vhd != nil { - vhdUri = *additionaldisk.Vhd.URI - s.say(fmt.Sprintf(" -> Additional Disk %d : '%s'", i+1, vhdUri)) - } else { - vhdUri = *additionaldisk.ManagedDisk.ID - s.say(fmt.Sprintf(" -> Managed Additional Disk %d : '%s'", i+1, vhdUri)) - } - additional_disks[i] = vhdUri - } - state.Put(constants.ArmAdditionalDiskVhds, additional_disks) - } - - return multistep.ActionContinue -} - -func (*StepGetDataDisk) Cleanup(multistep.StateBag) { -} diff --git a/builder/azure/arm/step_get_additional_disks_test.go b/builder/azure/arm/step_get_additional_disks_test.go deleted file mode 100644 index 511379fd9..000000000 --- a/builder/azure/arm/step_get_additional_disks_test.go +++ /dev/null @@ -1,131 +0,0 @@ -package arm - -import ( - "context" - "fmt" - "testing" - - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2018-04-01/compute" - - "github.com/hashicorp/packer/builder/azure/common/constants" - - "github.com/hashicorp/packer-plugin-sdk/multistep" -) - -func TestStepGetAdditionalDiskShouldFailIfGetFails(t *testing.T) { - var testSubject = &StepGetDataDisk{ - query: func(context.Context, string, string) (compute.VirtualMachine, error) { - return createVirtualMachineWithDataDisksFromUri("test.vhd"), fmt.Errorf("!! Unit Test FAIL !!") - }, - say: func(message string) {}, - error: func(e error) {}, - } - - stateBag := createTestStateBagStepGetAdditionalDisks() - - var result = testSubject.Run(context.Background(), stateBag) - if result != multistep.ActionHalt { - t.Fatalf("Expected the step to return 'ActionHalt', but got '%d'.", result) - } - - if _, ok := stateBag.GetOk(constants.Error); ok == false { - t.Fatalf("Expected the step to set stateBag['%s'], but it was not.", constants.Error) - } -} - -func TestStepGetAdditionalDiskShouldPassIfGetPasses(t *testing.T) { - var testSubject = &StepGetDataDisk{ - query: func(context.Context, string, string) (compute.VirtualMachine, error) { - return createVirtualMachineWithDataDisksFromUri("test.vhd"), nil - }, - say: func(message string) {}, - error: func(e error) {}, - } - - stateBag := createTestStateBagStepGetAdditionalDisks() - - var result = testSubject.Run(context.Background(), stateBag) - if result != multistep.ActionContinue { - t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result) - } - - if _, ok := stateBag.GetOk(constants.Error); ok == true { - t.Fatalf("Expected the step to not set stateBag['%s'], but it was.", constants.Error) - } -} - -func TestStepGetAdditionalDiskShouldTakeValidateArgumentsFromStateBag(t *testing.T) { - var actualResourceGroupName string - var actualComputeName string - - var testSubject = &StepGetDataDisk{ - query: func(ctx context.Context, resourceGroupName string, computeName string) (compute.VirtualMachine, error) { - actualResourceGroupName = resourceGroupName - actualComputeName = computeName - - return createVirtualMachineWithDataDisksFromUri("test.vhd"), nil - }, - say: func(message string) {}, - error: func(e error) {}, - } - - stateBag := createTestStateBagStepGetAdditionalDisks() - var result = testSubject.Run(context.Background(), stateBag) - - if result != multistep.ActionContinue { - t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result) - } - - var expectedComputeName = stateBag.Get(constants.ArmComputeName).(string) - var expectedResourceGroupName = stateBag.Get(constants.ArmResourceGroupName).(string) - - if actualComputeName != expectedComputeName { - t.Fatal("Expected the step to source 'constants.ArmResourceGroupName' from the state bag, but it did not.") - } - - if actualResourceGroupName != expectedResourceGroupName { - t.Fatal("Expected the step to source 'constants.ArmResourceGroupName' from the state bag, but it did not.") - } - - expectedAdditionalDiskVhds, ok := stateBag.GetOk(constants.ArmAdditionalDiskVhds) - if !ok { - t.Fatalf("Expected the state bag to have a value for '%s', but it did not.", constants.ArmAdditionalDiskVhds) - } - - expectedAdditionalDiskVhd := expectedAdditionalDiskVhds.([]string) - if expectedAdditionalDiskVhd[0] != "test.vhd" { - t.Fatalf("Expected the value of stateBag[%s] to be 'test.vhd', but got '%s'.", constants.ArmAdditionalDiskVhds, expectedAdditionalDiskVhd[0]) - } -} - -func createTestStateBagStepGetAdditionalDisks() multistep.StateBag { - stateBag := new(multistep.BasicStateBag) - - stateBag.Put(constants.ArmComputeName, "Unit Test: ComputeName") - stateBag.Put(constants.ArmResourceGroupName, "Unit Test: ResourceGroupName") - - return stateBag -} - -func createVirtualMachineWithDataDisksFromUri(vhdUri string) compute.VirtualMachine { - vm := compute.VirtualMachine{ - VirtualMachineProperties: &compute.VirtualMachineProperties{ - StorageProfile: &compute.StorageProfile{ - OsDisk: &compute.OSDisk{ - Vhd: &compute.VirtualHardDisk{ - URI: &vhdUri, - }, - }, - DataDisks: &[]compute.DataDisk{ - { - Vhd: &compute.VirtualHardDisk{ - URI: &vhdUri, - }, - }, - }, - }, - }, - } - - return vm -} diff --git a/builder/azure/arm/step_get_certificate.go b/builder/azure/arm/step_get_certificate.go deleted file mode 100644 index 9102f965a..000000000 --- a/builder/azure/arm/step_get_certificate.go +++ /dev/null @@ -1,77 +0,0 @@ -package arm - -import ( - "context" - "fmt" - "time" - - "github.com/hashicorp/packer-plugin-sdk/multistep" - packersdk "github.com/hashicorp/packer-plugin-sdk/packer" - "github.com/hashicorp/packer/builder/azure/common/constants" -) - -type StepGetCertificate struct { - client *AzureClient - get func(keyVaultName string, secretName string) (string, error) - say func(message string) - error func(e error) - pause func() -} - -func NewStepGetCertificate(client *AzureClient, ui packersdk.Ui) *StepGetCertificate { - var step = &StepGetCertificate{ - client: client, - say: func(message string) { ui.Say(message) }, - error: func(e error) { ui.Error(e.Error()) }, - pause: func() { time.Sleep(30 * time.Second) }, - } - - step.get = step.getCertificateUrl - return step -} - -func (s *StepGetCertificate) getCertificateUrl(keyVaultName string, secretName string) (string, error) { - secret, err := s.client.GetSecret(keyVaultName, secretName) - if err != nil { - s.say(s.client.LastError.Error()) - return "", err - } - - return *secret.ID, err -} - -func (s *StepGetCertificate) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { - s.say("Getting the certificate's URL ...") - - var keyVaultName = state.Get(constants.ArmKeyVaultName).(string) - - s.say(fmt.Sprintf(" -> Key Vault Name : '%s'", keyVaultName)) - s.say(fmt.Sprintf(" -> Key Vault Secret Name : '%s'", DefaultSecretName)) - - var err error - var url string - for i := 0; i < 5; i++ { - url, err = s.get(keyVaultName, DefaultSecretName) - if err == nil { - break - } - - s.say(fmt.Sprintf(" ...failed to get certificate URL, retry(%d)", i)) - s.pause() - } - - if err != nil { - state.Put(constants.Error, err) - s.error(err) - - return multistep.ActionHalt - } - - s.say(fmt.Sprintf(" -> Certificate URL : '%s'", url)) - state.Put(constants.ArmCertificateUrl, url) - - return multistep.ActionContinue -} - -func (*StepGetCertificate) Cleanup(multistep.StateBag) { -} diff --git a/builder/azure/arm/step_get_certificate_test.go b/builder/azure/arm/step_get_certificate_test.go deleted file mode 100644 index 640fda60a..000000000 --- a/builder/azure/arm/step_get_certificate_test.go +++ /dev/null @@ -1,99 +0,0 @@ -package arm - -import ( - "context" - "fmt" - "testing" - - "github.com/hashicorp/packer-plugin-sdk/multistep" - "github.com/hashicorp/packer/builder/azure/common/constants" -) - -func TestStepGetCertificateShouldFailIfGetFails(t *testing.T) { - var testSubject = &StepGetCertificate{ - get: func(string, string) (string, error) { return "", fmt.Errorf("!! Unit Test FAIL !!") }, - say: func(message string) {}, - error: func(e error) {}, - pause: func() {}, - } - - stateBag := createTestStateBagStepGetCertificate() - - var result = testSubject.Run(context.Background(), stateBag) - if result != multistep.ActionHalt { - t.Fatalf("Expected the step to return 'ActionHalt', but got '%d'.", result) - } - - if _, ok := stateBag.GetOk(constants.Error); ok == false { - t.Fatalf("Expected the step to set stateBag['%s'], but it was not.", constants.Error) - } -} - -func TestStepGetCertificateShouldPassIfGetPasses(t *testing.T) { - var testSubject = &StepGetCertificate{ - get: func(string, string) (string, error) { return "", nil }, - say: func(message string) {}, - error: func(e error) {}, - pause: func() {}, - } - - stateBag := createTestStateBagStepGetCertificate() - - var result = testSubject.Run(context.Background(), stateBag) - if result != multistep.ActionContinue { - t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result) - } - - if _, ok := stateBag.GetOk(constants.Error); ok == true { - t.Fatalf("Expected the step to not set stateBag['%s'], but it was.", constants.Error) - } -} - -func TestStepGetCertificateShouldTakeStepArgumentsFromStateBag(t *testing.T) { - var actualKeyVaultName string - var actualSecretName string - - var testSubject = &StepGetCertificate{ - get: func(keyVaultName string, secretName string) (string, error) { - actualKeyVaultName = keyVaultName - actualSecretName = secretName - - return "http://key.vault/1", nil - }, - say: func(message string) {}, - error: func(e error) {}, - pause: func() {}, - } - - stateBag := createTestStateBagStepGetCertificate() - var result = testSubject.Run(context.Background(), stateBag) - - if result != multistep.ActionContinue { - t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result) - } - - var expectedKeyVaultName = stateBag.Get(constants.ArmKeyVaultName).(string) - - if actualKeyVaultName != expectedKeyVaultName { - t.Fatal("Expected StepGetCertificate to source 'constants.ArmKeyVaultName' from the state bag, but it did not.") - } - if actualSecretName != DefaultSecretName { - t.Fatal("Expected StepGetCertificate to use default value for secret, but it did not.") - } - - expectedCertificateUrl, ok := stateBag.GetOk(constants.ArmCertificateUrl) - if !ok { - t.Fatalf("Expected the state bag to have a value for '%s', but it did not.", constants.ArmCertificateUrl) - } - - if expectedCertificateUrl != "http://key.vault/1" { - t.Fatalf("Expected the value of stateBag[%s] to be 'http://key.vault/1', but got '%s'.", constants.ArmCertificateUrl, expectedCertificateUrl) - } -} - -func createTestStateBagStepGetCertificate() multistep.StateBag { - stateBag := new(multistep.BasicStateBag) - stateBag.Put(constants.ArmKeyVaultName, "Unit Test: KeyVaultName") - - return stateBag -} diff --git a/builder/azure/arm/step_get_ip_address.go b/builder/azure/arm/step_get_ip_address.go deleted file mode 100644 index 6a5ace4c1..000000000 --- a/builder/azure/arm/step_get_ip_address.go +++ /dev/null @@ -1,107 +0,0 @@ -package arm - -import ( - "context" - "fmt" - - "github.com/hashicorp/packer-plugin-sdk/multistep" - packersdk "github.com/hashicorp/packer-plugin-sdk/packer" - "github.com/hashicorp/packer/builder/azure/common/constants" -) - -type EndpointType int - -const ( - PublicEndpoint EndpointType = iota - PrivateEndpoint - PublicEndpointInPrivateNetwork -) - -var ( - EndpointCommunicationText = map[EndpointType]string{ - PublicEndpoint: "PublicEndpoint", - PrivateEndpoint: "PrivateEndpoint", - PublicEndpointInPrivateNetwork: "PublicEndpointInPrivateNetwork", - } -) - -type StepGetIPAddress struct { - client *AzureClient - endpoint EndpointType - get func(ctx context.Context, resourceGroupName string, ipAddressName string, interfaceName string) (string, error) - say func(message string) - error func(e error) -} - -func NewStepGetIPAddress(client *AzureClient, ui packersdk.Ui, endpoint EndpointType) *StepGetIPAddress { - var step = &StepGetIPAddress{ - client: client, - endpoint: endpoint, - say: func(message string) { ui.Say(message) }, - error: func(e error) { ui.Error(e.Error()) }, - } - - switch endpoint { - case PrivateEndpoint: - step.get = step.getPrivateIP - case PublicEndpoint: - step.get = step.getPublicIP - case PublicEndpointInPrivateNetwork: - step.get = step.getPublicIPInPrivateNetwork - } - - return step -} - -func (s *StepGetIPAddress) getPrivateIP(ctx context.Context, resourceGroupName string, ipAddressName string, interfaceName string) (string, error) { - resp, err := s.client.InterfacesClient.Get(ctx, resourceGroupName, interfaceName, "") - if err != nil { - s.say(s.client.LastError.Error()) - return "", err - } - - return *(*resp.IPConfigurations)[0].PrivateIPAddress, nil -} - -func (s *StepGetIPAddress) getPublicIP(ctx context.Context, resourceGroupName string, ipAddressName string, interfaceName string) (string, error) { - resp, err := s.client.PublicIPAddressesClient.Get(ctx, resourceGroupName, ipAddressName, "") - if err != nil { - return "", err - } - - return *resp.IPAddress, nil -} - -func (s *StepGetIPAddress) getPublicIPInPrivateNetwork(ctx context.Context, resourceGroupName string, ipAddressName string, interfaceName string) (string, error) { - s.getPrivateIP(ctx, resourceGroupName, ipAddressName, interfaceName) - return s.getPublicIP(ctx, resourceGroupName, ipAddressName, interfaceName) -} - -func (s *StepGetIPAddress) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { - s.say("Getting the VM's IP address ...") - - var resourceGroupName = state.Get(constants.ArmResourceGroupName).(string) - var ipAddressName = state.Get(constants.ArmPublicIPAddressName).(string) - var nicName = state.Get(constants.ArmNicName).(string) - - s.say(fmt.Sprintf(" -> ResourceGroupName : '%s'", resourceGroupName)) - s.say(fmt.Sprintf(" -> PublicIPAddressName : '%s'", ipAddressName)) - s.say(fmt.Sprintf(" -> NicName : '%s'", nicName)) - s.say(fmt.Sprintf(" -> Network Connection : '%s'", EndpointCommunicationText[s.endpoint])) - - address, err := s.get(ctx, resourceGroupName, ipAddressName, nicName) - if err != nil { - state.Put(constants.Error, err) - s.error(err) - - return multistep.ActionHalt - } - - state.Put(constants.SSHHost, address) - s.say(fmt.Sprintf(" -> IP Address : '%s'", address)) - - return multistep.ActionContinue -} - -func (*StepGetIPAddress) Cleanup(multistep.StateBag) { -} diff --git a/builder/azure/arm/step_get_ip_address_test.go b/builder/azure/arm/step_get_ip_address_test.go deleted file mode 100644 index bb5beb5bf..000000000 --- a/builder/azure/arm/step_get_ip_address_test.go +++ /dev/null @@ -1,124 +0,0 @@ -package arm - -import ( - "context" - "fmt" - "testing" - - "github.com/hashicorp/packer-plugin-sdk/multistep" - "github.com/hashicorp/packer/builder/azure/common/constants" -) - -func TestStepGetIPAddressShouldFailIfGetFails(t *testing.T) { - endpoints := []EndpointType{PublicEndpoint, PublicEndpointInPrivateNetwork} - - for _, endpoint := range endpoints { - var testSubject = &StepGetIPAddress{ - get: func(context.Context, string, string, string) (string, error) { - return "", fmt.Errorf("!! Unit Test FAIL !!") - }, - endpoint: endpoint, - say: func(message string) {}, - error: func(e error) {}, - } - - stateBag := createTestStateBagStepGetIPAddress() - - var result = testSubject.Run(context.Background(), stateBag) - if result != multistep.ActionHalt { - t.Fatalf("Expected the step to return 'ActionHalt', but got '%d'.", result) - } - - if _, ok := stateBag.GetOk(constants.Error); ok == false { - t.Fatalf("Expected the step to set stateBag['%s'], but it was not.", constants.Error) - } - } -} - -func TestStepGetIPAddressShouldPassIfGetPasses(t *testing.T) { - endpoints := []EndpointType{PublicEndpoint, PublicEndpointInPrivateNetwork} - - for _, endpoint := range endpoints { - var testSubject = &StepGetIPAddress{ - get: func(context.Context, string, string, string) (string, error) { return "", nil }, - endpoint: endpoint, - say: func(message string) {}, - error: func(e error) {}, - } - - stateBag := createTestStateBagStepGetIPAddress() - - var result = testSubject.Run(context.Background(), stateBag) - if result != multistep.ActionContinue { - t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result) - } - - if _, ok := stateBag.GetOk(constants.Error); ok == true { - t.Fatalf("Expected the step to not set stateBag['%s'], but it was.", constants.Error) - } - } -} - -func TestStepGetIPAddressShouldTakeStepArgumentsFromStateBag(t *testing.T) { - var actualResourceGroupName string - var actualIPAddressName string - var actualNicName string - endpoints := []EndpointType{PublicEndpoint, PublicEndpointInPrivateNetwork} - - for _, endpoint := range endpoints { - var testSubject = &StepGetIPAddress{ - get: func(ctx context.Context, resourceGroupName string, ipAddressName string, nicName string) (string, error) { - actualResourceGroupName = resourceGroupName - actualIPAddressName = ipAddressName - actualNicName = nicName - - return "127.0.0.1", nil - }, - endpoint: endpoint, - say: func(message string) {}, - error: func(e error) {}, - } - - stateBag := createTestStateBagStepGetIPAddress() - var result = testSubject.Run(context.Background(), stateBag) - - if result != multistep.ActionContinue { - t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result) - } - - var expectedResourceGroupName = stateBag.Get(constants.ArmResourceGroupName).(string) - var expectedIPAddressName = stateBag.Get(constants.ArmPublicIPAddressName).(string) - var expectedNicName = stateBag.Get(constants.ArmNicName).(string) - - if actualIPAddressName != expectedIPAddressName { - t.Fatal("Expected StepGetIPAddress to source 'constants.ArmIPAddressName' from the state bag, but it did not.") - } - - if actualResourceGroupName != expectedResourceGroupName { - t.Fatal("Expected StepGetIPAddress to source 'constants.ArmResourceGroupName' from the state bag, but it did not.") - } - - if actualNicName != expectedNicName { - t.Fatalf("Expected StepGetIPAddress to source 'constants.ArmNetworkInterfaceName' from the state bag, but it did not.") - } - - expectedIPAddress, ok := stateBag.GetOk(constants.SSHHost) - if !ok { - t.Fatalf("Expected the state bag to have a value for '%s', but it did not.", constants.SSHHost) - } - - if expectedIPAddress != "127.0.0.1" { - t.Fatalf("Expected the value of stateBag[%s] to be '127.0.0.1', but got '%s'.", constants.SSHHost, expectedIPAddress) - } - } -} - -func createTestStateBagStepGetIPAddress() multistep.StateBag { - stateBag := new(multistep.BasicStateBag) - - stateBag.Put(constants.ArmPublicIPAddressName, "Unit Test: PublicIPAddressName") - stateBag.Put(constants.ArmNicName, "Unit Test: NicName") - stateBag.Put(constants.ArmResourceGroupName, "Unit Test: ResourceGroupName") - - return stateBag -} diff --git a/builder/azure/arm/step_get_os_disk.go b/builder/azure/arm/step_get_os_disk.go deleted file mode 100644 index 719f7e3a3..000000000 --- a/builder/azure/arm/step_get_os_disk.go +++ /dev/null @@ -1,72 +0,0 @@ -package arm - -import ( - "context" - "fmt" - - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2018-04-01/compute" - - "github.com/hashicorp/packer/builder/azure/common/constants" - - "github.com/hashicorp/packer-plugin-sdk/multistep" - packersdk "github.com/hashicorp/packer-plugin-sdk/packer" -) - -type StepGetOSDisk struct { - client *AzureClient - query func(ctx context.Context, resourceGroupName string, computeName string) (compute.VirtualMachine, error) - say func(message string) - error func(e error) -} - -func NewStepGetOSDisk(client *AzureClient, ui packersdk.Ui) *StepGetOSDisk { - var step = &StepGetOSDisk{ - client: client, - say: func(message string) { ui.Say(message) }, - error: func(e error) { ui.Error(e.Error()) }, - } - - step.query = step.queryCompute - return step -} - -func (s *StepGetOSDisk) queryCompute(ctx context.Context, resourceGroupName string, computeName string) (compute.VirtualMachine, error) { - vm, err := s.client.VirtualMachinesClient.Get(ctx, resourceGroupName, computeName, "") - if err != nil { - s.say(s.client.LastError.Error()) - } - return vm, err -} - -func (s *StepGetOSDisk) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { - s.say("Querying the machine's properties ...") - - var resourceGroupName = state.Get(constants.ArmResourceGroupName).(string) - var computeName = state.Get(constants.ArmComputeName).(string) - - s.say(fmt.Sprintf(" -> ResourceGroupName : '%s'", resourceGroupName)) - s.say(fmt.Sprintf(" -> ComputeName : '%s'", computeName)) - - vm, err := s.query(ctx, resourceGroupName, computeName) - if err != nil { - state.Put(constants.Error, err) - s.error(err) - - return multistep.ActionHalt - } - - var vhdUri string - if vm.StorageProfile.OsDisk.Vhd != nil { - vhdUri = *vm.StorageProfile.OsDisk.Vhd.URI - s.say(fmt.Sprintf(" -> OS Disk : '%s'", vhdUri)) - } else { - vhdUri = *vm.StorageProfile.OsDisk.ManagedDisk.ID - s.say(fmt.Sprintf(" -> Managed OS Disk : '%s'", vhdUri)) - } - - state.Put(constants.ArmOSDiskVhd, vhdUri) - return multistep.ActionContinue -} - -func (*StepGetOSDisk) Cleanup(multistep.StateBag) { -} diff --git a/builder/azure/arm/step_get_os_disk_test.go b/builder/azure/arm/step_get_os_disk_test.go deleted file mode 100644 index 3c229dced..000000000 --- a/builder/azure/arm/step_get_os_disk_test.go +++ /dev/null @@ -1,123 +0,0 @@ -package arm - -import ( - "context" - "fmt" - "testing" - - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2018-04-01/compute" - - "github.com/hashicorp/packer/builder/azure/common/constants" - - "github.com/hashicorp/packer-plugin-sdk/multistep" -) - -func TestStepGetOSDiskShouldFailIfGetFails(t *testing.T) { - var testSubject = &StepGetOSDisk{ - query: func(context.Context, string, string) (compute.VirtualMachine, error) { - return createVirtualMachineFromUri("test.vhd"), fmt.Errorf("!! Unit Test FAIL !!") - }, - say: func(message string) {}, - error: func(e error) {}, - } - - stateBag := createTestStateBagStepGetOSDisk() - - var result = testSubject.Run(context.Background(), stateBag) - if result != multistep.ActionHalt { - t.Fatalf("Expected the step to return 'ActionHalt', but got '%d'.", result) - } - - if _, ok := stateBag.GetOk(constants.Error); ok == false { - t.Fatalf("Expected the step to set stateBag['%s'], but it was not.", constants.Error) - } -} - -func TestStepGetOSDiskShouldPassIfGetPasses(t *testing.T) { - var testSubject = &StepGetOSDisk{ - query: func(context.Context, string, string) (compute.VirtualMachine, error) { - return createVirtualMachineFromUri("test.vhd"), nil - }, - say: func(message string) {}, - error: func(e error) {}, - } - - stateBag := createTestStateBagStepGetOSDisk() - - var result = testSubject.Run(context.Background(), stateBag) - if result != multistep.ActionContinue { - t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result) - } - - if _, ok := stateBag.GetOk(constants.Error); ok == true { - t.Fatalf("Expected the step to not set stateBag['%s'], but it was.", constants.Error) - } -} - -func TestStepGetOSDiskShouldTakeValidateArgumentsFromStateBag(t *testing.T) { - var actualResourceGroupName string - var actualComputeName string - - var testSubject = &StepGetOSDisk{ - query: func(ctx context.Context, resourceGroupName string, computeName string) (compute.VirtualMachine, error) { - actualResourceGroupName = resourceGroupName - actualComputeName = computeName - - return createVirtualMachineFromUri("test.vhd"), nil - }, - say: func(message string) {}, - error: func(e error) {}, - } - - stateBag := createTestStateBagStepGetOSDisk() - var result = testSubject.Run(context.Background(), stateBag) - - if result != multistep.ActionContinue { - t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result) - } - - var expectedComputeName = stateBag.Get(constants.ArmComputeName).(string) - var expectedResourceGroupName = stateBag.Get(constants.ArmResourceGroupName).(string) - - if actualComputeName != expectedComputeName { - t.Fatal("Expected the step to source 'constants.ArmResourceGroupName' from the state bag, but it did not.") - } - - if actualResourceGroupName != expectedResourceGroupName { - t.Fatal("Expected the step to source 'constants.ArmResourceGroupName' from the state bag, but it did not.") - } - - expectedOSDiskVhd, ok := stateBag.GetOk(constants.ArmOSDiskVhd) - if !ok { - t.Fatalf("Expected the state bag to have a value for '%s', but it did not.", constants.ArmOSDiskVhd) - } - - if expectedOSDiskVhd != "test.vhd" { - t.Fatalf("Expected the value of stateBag[%s] to be 'test.vhd', but got '%s'.", constants.ArmOSDiskVhd, expectedOSDiskVhd) - } -} - -func createTestStateBagStepGetOSDisk() multistep.StateBag { - stateBag := new(multistep.BasicStateBag) - - stateBag.Put(constants.ArmComputeName, "Unit Test: ComputeName") - stateBag.Put(constants.ArmResourceGroupName, "Unit Test: ResourceGroupName") - - return stateBag -} - -func createVirtualMachineFromUri(vhdUri string) compute.VirtualMachine { - vm := compute.VirtualMachine{ - VirtualMachineProperties: &compute.VirtualMachineProperties{ - StorageProfile: &compute.StorageProfile{ - OsDisk: &compute.OSDisk{ - Vhd: &compute.VirtualHardDisk{ - URI: &vhdUri, - }, - }, - }, - }, - } - - return vm -} diff --git a/builder/azure/arm/step_power_off_compute.go b/builder/azure/arm/step_power_off_compute.go deleted file mode 100644 index 2cd126aeb..000000000 --- a/builder/azure/arm/step_power_off_compute.go +++ /dev/null @@ -1,56 +0,0 @@ -package arm - -import ( - "context" - "fmt" - - "github.com/hashicorp/packer-plugin-sdk/multistep" - packersdk "github.com/hashicorp/packer-plugin-sdk/packer" - "github.com/hashicorp/packer/builder/azure/common/constants" -) - -type StepPowerOffCompute struct { - client *AzureClient - powerOff func(ctx context.Context, resourceGroupName string, computeName string) error - say func(message string) - error func(e error) -} - -func NewStepPowerOffCompute(client *AzureClient, ui packersdk.Ui) *StepPowerOffCompute { - var step = &StepPowerOffCompute{ - client: client, - say: func(message string) { ui.Say(message) }, - error: func(e error) { ui.Error(e.Error()) }, - } - - step.powerOff = step.powerOffCompute - return step -} - -func (s *StepPowerOffCompute) powerOffCompute(ctx context.Context, resourceGroupName string, computeName string) error { - f, err := s.client.VirtualMachinesClient.Deallocate(ctx, resourceGroupName, computeName) - if err == nil { - err = f.WaitForCompletionRef(ctx, s.client.VirtualMachinesClient.Client) - } - if err != nil { - s.say(s.client.LastError.Error()) - } - return err -} - -func (s *StepPowerOffCompute) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { - s.say("Powering off machine ...") - - var resourceGroupName = state.Get(constants.ArmResourceGroupName).(string) - var computeName = state.Get(constants.ArmComputeName).(string) - - s.say(fmt.Sprintf(" -> ResourceGroupName : '%s'", resourceGroupName)) - s.say(fmt.Sprintf(" -> ComputeName : '%s'", computeName)) - - err := s.powerOff(ctx, resourceGroupName, computeName) - - return processStepResult(err, s.error, state) -} - -func (*StepPowerOffCompute) Cleanup(multistep.StateBag) { -} diff --git a/builder/azure/arm/step_power_off_compute_test.go b/builder/azure/arm/step_power_off_compute_test.go deleted file mode 100644 index 69235a289..000000000 --- a/builder/azure/arm/step_power_off_compute_test.go +++ /dev/null @@ -1,91 +0,0 @@ -package arm - -import ( - "context" - "fmt" - "testing" - - "github.com/hashicorp/packer-plugin-sdk/multistep" - "github.com/hashicorp/packer/builder/azure/common/constants" -) - -func TestStepPowerOffComputeShouldFailIfPowerOffFails(t *testing.T) { - var testSubject = &StepPowerOffCompute{ - powerOff: func(context.Context, string, string) error { return fmt.Errorf("!! Unit Test FAIL !!") }, - say: func(message string) {}, - error: func(e error) {}, - } - - stateBag := createTestStateBagStepPowerOffCompute() - - var result = testSubject.Run(context.Background(), stateBag) - if result != multistep.ActionHalt { - t.Fatalf("Expected the step to return 'ActionHalt', but got '%d'.", result) - } - - if _, ok := stateBag.GetOk(constants.Error); ok == false { - t.Fatalf("Expected the step to set stateBag['%s'], but it was not.", constants.Error) - } -} - -func TestStepPowerOffComputeShouldPassIfPowerOffPasses(t *testing.T) { - var testSubject = &StepPowerOffCompute{ - powerOff: func(context.Context, string, string) error { return nil }, - say: func(message string) {}, - error: func(e error) {}, - } - - stateBag := createTestStateBagStepPowerOffCompute() - - var result = testSubject.Run(context.Background(), stateBag) - if result != multistep.ActionContinue { - t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result) - } - - if _, ok := stateBag.GetOk(constants.Error); ok == true { - t.Fatalf("Expected the step to not set stateBag['%s'], but it was.", constants.Error) - } -} - -func TestStepPowerOffComputeShouldTakeStepArgumentsFromStateBag(t *testing.T) { - var actualResourceGroupName string - var actualComputeName string - - var testSubject = &StepPowerOffCompute{ - powerOff: func(ctx context.Context, resourceGroupName string, computeName string) error { - actualResourceGroupName = resourceGroupName - actualComputeName = computeName - - return nil - }, - say: func(message string) {}, - error: func(e error) {}, - } - - stateBag := createTestStateBagStepPowerOffCompute() - var result = testSubject.Run(context.Background(), stateBag) - - if result != multistep.ActionContinue { - t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result) - } - - var expectedComputeName = stateBag.Get(constants.ArmComputeName).(string) - var expectedResourceGroupName = stateBag.Get(constants.ArmResourceGroupName).(string) - - if actualComputeName != expectedComputeName { - t.Fatal("Expected the step to source 'constants.ArmResourceGroupName' from the state bag, but it did not.") - } - - if actualResourceGroupName != expectedResourceGroupName { - t.Fatal("Expected the step to source 'constants.ArmResourceGroupName' from the state bag, but it did not.") - } -} - -func createTestStateBagStepPowerOffCompute() multistep.StateBag { - stateBag := new(multistep.BasicStateBag) - - stateBag.Put(constants.ArmComputeName, "Unit Test: ComputeName") - stateBag.Put(constants.ArmResourceGroupName, "Unit Test: ResourceGroupName") - - return stateBag -} diff --git a/builder/azure/arm/step_publish_to_shared_image_gallery.go b/builder/azure/arm/step_publish_to_shared_image_gallery.go deleted file mode 100644 index 629858e4c..000000000 --- a/builder/azure/arm/step_publish_to_shared_image_gallery.go +++ /dev/null @@ -1,192 +0,0 @@ -package arm - -import ( - "context" - "fmt" - - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-03-01/compute" - "github.com/Azure/go-autorest/autorest/date" - "github.com/hashicorp/packer-plugin-sdk/multistep" - packersdk "github.com/hashicorp/packer-plugin-sdk/packer" - "github.com/hashicorp/packer/builder/azure/common/constants" -) - -type StepPublishToSharedImageGallery struct { - client *AzureClient - publish func(ctx context.Context, mdiID string, sharedImageGallery SharedImageGalleryDestination, miSGImageVersionEndOfLifeDate string, miSGImageVersionExcludeFromLatest bool, miSigReplicaCount int32, location string, tags map[string]*string) (string, error) - say func(message string) - error func(e error) - toSIG func() bool -} - -func NewStepPublishToSharedImageGallery(client *AzureClient, ui packersdk.Ui, config *Config) *StepPublishToSharedImageGallery { - var step = &StepPublishToSharedImageGallery{ - client: client, - say: func(message string) { - ui.Say(message) - }, - error: func(e error) { - ui.Error(e.Error()) - }, - toSIG: func() bool { - return config.isManagedImage() && config.SharedGalleryDestination.SigDestinationGalleryName != "" - }, - } - - step.publish = step.publishToSig - return step -} - -func getSigDestinationStorageAccountType(s string) (compute.StorageAccountType, error) { - switch s { - case "", string(compute.StorageAccountTypeStandardLRS): - return compute.StorageAccountTypeStandardLRS, nil - case string(compute.StorageAccountTypeStandardZRS): - return compute.StorageAccountTypeStandardZRS, nil - default: - return "", fmt.Errorf("not an accepted value for shared_image_gallery_destination.storage_account_type") - } -} - -func getSigDestination(state multistep.StateBag) SharedImageGalleryDestination { - subscription := state.Get(constants.ArmManagedImageSubscription).(string) - resourceGroup := state.Get(constants.ArmManagedImageSigPublishResourceGroup).(string) - galleryName := state.Get(constants.ArmManagedImageSharedGalleryName).(string) - imageName := state.Get(constants.ArmManagedImageSharedGalleryImageName).(string) - imageVersion := state.Get(constants.ArmManagedImageSharedGalleryImageVersion).(string) - replicationRegions := state.Get(constants.ArmManagedImageSharedGalleryReplicationRegions).([]string) - storageAccountType := state.Get(constants.ArmManagedImageSharedGalleryImageVersionStorageAccountType).(string) - - return SharedImageGalleryDestination{ - SigDestinationSubscription: subscription, - SigDestinationResourceGroup: resourceGroup, - SigDestinationGalleryName: galleryName, - SigDestinationImageName: imageName, - SigDestinationImageVersion: imageVersion, - SigDestinationReplicationRegions: replicationRegions, - SigDestinationStorageAccountType: storageAccountType, - } -} - -func (s *StepPublishToSharedImageGallery) publishToSig(ctx context.Context, mdiID string, sharedImageGallery SharedImageGalleryDestination, miSGImageVersionEndOfLifeDate string, miSGImageVersionExcludeFromLatest bool, miSigReplicaCount int32, location string, tags map[string]*string) (string, error) { - replicationRegions := make([]compute.TargetRegion, len(sharedImageGallery.SigDestinationReplicationRegions)) - for i, v := range sharedImageGallery.SigDestinationReplicationRegions { - regionName := v - replicationRegions[i] = compute.TargetRegion{Name: ®ionName} - } - - var endOfLifeDate *date.Time - if miSGImageVersionEndOfLifeDate != "" { - parseDate, err := date.ParseTime("2006-01-02T15:04:05.99Z", miSGImageVersionEndOfLifeDate) - if err != nil { - s.say(fmt.Sprintf("Error parsing date from shared_gallery_image_version_end_of_life_date: %s", err)) - return "", err - } - endOfLifeDate = &date.Time{Time: parseDate} - } else { - endOfLifeDate = (*date.Time)(nil) - } - - storageAccountType, err := getSigDestinationStorageAccountType(string(sharedImageGallery.SigDestinationStorageAccountType)) - if err != nil { - s.error(err) - return "", err - } - - galleryImageVersion := compute.GalleryImageVersion{ - Location: &location, - Tags: tags, - GalleryImageVersionProperties: &compute.GalleryImageVersionProperties{ - PublishingProfile: &compute.GalleryImageVersionPublishingProfile{ - Source: &compute.GalleryArtifactSource{ - ManagedImage: &compute.ManagedArtifact{ - ID: &mdiID, - }, - }, - TargetRegions: &replicationRegions, - EndOfLifeDate: endOfLifeDate, - ExcludeFromLatest: &miSGImageVersionExcludeFromLatest, - ReplicaCount: &miSigReplicaCount, - StorageAccountType: storageAccountType, - }, - }, - } - - f, err := s.client.GalleryImageVersionsClient.CreateOrUpdate(ctx, sharedImageGallery.SigDestinationResourceGroup, sharedImageGallery.SigDestinationGalleryName, sharedImageGallery.SigDestinationImageName, sharedImageGallery.SigDestinationImageVersion, galleryImageVersion) - - if err != nil { - s.say(s.client.LastError.Error()) - return "", err - } - - err = f.WaitForCompletionRef(ctx, s.client.GalleryImageVersionsClient.Client) - - if err != nil { - s.say(s.client.LastError.Error()) - return "", err - } - - createdSGImageVersion, err := f.Result(s.client.GalleryImageVersionsClient) - - if err != nil { - s.say(s.client.LastError.Error()) - return "", err - } - - s.say(fmt.Sprintf(" -> Shared Gallery Image Version ID : '%s'", *(createdSGImageVersion.ID))) - return *(createdSGImageVersion.ID), nil -} - -func (s *StepPublishToSharedImageGallery) Run(ctx context.Context, stateBag multistep.StateBag) multistep.StepAction { - if !s.toSIG() { - return multistep.ActionContinue - } - - s.say("Publishing to Shared Image Gallery ...") - - location := stateBag.Get(constants.ArmLocation).(string) - tags := stateBag.Get(constants.ArmTags).(map[string]*string) - - sharedImageGallery := getSigDestination(stateBag) - targetManagedImageResourceGroupName := stateBag.Get(constants.ArmManagedImageResourceGroupName).(string) - targetManagedImageName := stateBag.Get(constants.ArmManagedImageName).(string) - - managedImageSubscription := stateBag.Get(constants.ArmManagedImageSubscription).(string) - mdiID := fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Compute/images/%s", managedImageSubscription, targetManagedImageResourceGroupName, targetManagedImageName) - - miSGImageVersionEndOfLifeDate, _ := stateBag.Get(constants.ArmManagedImageSharedGalleryImageVersionEndOfLifeDate).(string) - miSGImageVersionExcludeFromLatest, _ := stateBag.Get(constants.ArmManagedImageSharedGalleryImageVersionExcludeFromLatest).(bool) - miSigReplicaCount, _ := stateBag.Get(constants.ArmManagedImageSharedGalleryImageVersionReplicaCount).(int32) - // Replica count must be between 1 and 10 inclusive. - if miSigReplicaCount <= 0 { - miSigReplicaCount = constants.SharedImageGalleryImageVersionDefaultMinReplicaCount - } else if miSigReplicaCount > 10 { - miSigReplicaCount = constants.SharedImageGalleryImageVersionDefaultMaxReplicaCount - } - - s.say(fmt.Sprintf(" -> MDI ID used for SIG publish : '%s'", mdiID)) - s.say(fmt.Sprintf(" -> SIG publish resource group : '%s'", sharedImageGallery.SigDestinationResourceGroup)) - s.say(fmt.Sprintf(" -> SIG gallery name : '%s'", sharedImageGallery.SigDestinationGalleryName)) - s.say(fmt.Sprintf(" -> SIG image name : '%s'", sharedImageGallery.SigDestinationImageName)) - s.say(fmt.Sprintf(" -> SIG image version : '%s'", sharedImageGallery.SigDestinationImageVersion)) - s.say(fmt.Sprintf(" -> SIG replication regions : '%v'", sharedImageGallery.SigDestinationReplicationRegions)) - s.say(fmt.Sprintf(" -> SIG storage account type : '%s'", sharedImageGallery.SigDestinationStorageAccountType)) - s.say(fmt.Sprintf(" -> SIG image version endoflife date : '%s'", miSGImageVersionEndOfLifeDate)) - s.say(fmt.Sprintf(" -> SIG image version exclude from latest : '%t'", miSGImageVersionExcludeFromLatest)) - s.say(fmt.Sprintf(" -> SIG replica count [1, 10] : '%d'", miSigReplicaCount)) - - createdGalleryImageVersionID, err := s.publish(ctx, mdiID, sharedImageGallery, miSGImageVersionEndOfLifeDate, miSGImageVersionExcludeFromLatest, miSigReplicaCount, location, tags) - - if err != nil { - stateBag.Put(constants.Error, err) - s.error(err) - - return multistep.ActionHalt - } - - stateBag.Put(constants.ArmManagedImageSharedGalleryId, createdGalleryImageVersionID) - return multistep.ActionContinue -} - -func (*StepPublishToSharedImageGallery) Cleanup(multistep.StateBag) { -} diff --git a/builder/azure/arm/step_publish_to_shared_image_gallery_test.go b/builder/azure/arm/step_publish_to_shared_image_gallery_test.go deleted file mode 100644 index 87d636931..000000000 --- a/builder/azure/arm/step_publish_to_shared_image_gallery_test.go +++ /dev/null @@ -1,86 +0,0 @@ -package arm - -import ( - "context" - "testing" - - "github.com/hashicorp/packer-plugin-sdk/multistep" - "github.com/hashicorp/packer/builder/azure/common/constants" -) - -func TestStepPublishToSharedImageGalleryShouldNotPublishForVhd(t *testing.T) { - var testSubject = &StepPublishToSharedImageGallery{ - publish: func(context.Context, string, SharedImageGalleryDestination, string, bool, int32, string, map[string]*string) (string, error) { - return "test", nil - }, - say: func(message string) {}, - error: func(e error) {}, - toSIG: func() bool { return false }, - } - - stateBag := createTestStateBagStepPublishToSharedImageGalleryForVhd() - var result = testSubject.Run(context.Background(), stateBag) - if result != multistep.ActionContinue { - t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result) - } - - if _, ok := stateBag.GetOk(constants.Error); ok == true { - t.Fatalf("Expected the step to not set stateBag['%s'], but it was.", constants.Error) - } -} - -func TestStepPublishToSharedImageGalleryShouldPublishForManagedImageWithSig(t *testing.T) { - var testSubject = &StepPublishToSharedImageGallery{ - publish: func(context.Context, string, SharedImageGalleryDestination, string, bool, int32, string, map[string]*string) (string, error) { - return "", nil - }, - say: func(message string) {}, - error: func(e error) {}, - toSIG: func() bool { return true }, - } - - stateBag := createTestStateBagStepPublishToSharedImageGallery() - var result = testSubject.Run(context.Background(), stateBag) - if result != multistep.ActionContinue { - t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result) - } - - if _, ok := stateBag.GetOk(constants.Error); ok == true { - t.Fatalf("Expected the step to not set stateBag['%s'], but it was.", constants.Error) - } -} - -func createTestStateBagStepPublishToSharedImageGallery() multistep.StateBag { - stateBag := new(multistep.BasicStateBag) - - stateBag.Put(constants.ArmManagedImageSigPublishResourceGroup, "Unit Test: ManagedImageSigPublishResourceGroup") - stateBag.Put(constants.ArmManagedImageSharedGalleryName, "Unit Test: ManagedImageSharedGalleryName") - stateBag.Put(constants.ArmManagedImageSharedGalleryImageName, "Unit Test: ManagedImageSharedGalleryImageName") - stateBag.Put(constants.ArmManagedImageSharedGalleryImageVersion, "Unit Test: ManagedImageSharedGalleryImageVersion") - stateBag.Put(constants.ArmLocation, "Unit Test: Location") - value := "Unit Test: Tags" - tags := map[string]*string{ - "tag01": &value, - } - stateBag.Put(constants.ArmTags, tags) - stateBag.Put(constants.ArmManagedImageSharedGalleryReplicationRegions, []string{"ManagedImageSharedGalleryReplicationRegionA", "ManagedImageSharedGalleryReplicationRegionB"}) - stateBag.Put(constants.ArmManagedImageSharedGalleryImageVersionStorageAccountType, "Standard_LRS") - stateBag.Put(constants.ArmManagedImageResourceGroupName, "Unit Test: ManagedImageResourceGroupName") - stateBag.Put(constants.ArmManagedImageName, "Unit Test: ManagedImageName") - stateBag.Put(constants.ArmManagedImageSubscription, "Unit Test: ManagedImageSubscription") - - return stateBag -} - -func createTestStateBagStepPublishToSharedImageGalleryForVhd() multistep.StateBag { - stateBag := new(multistep.BasicStateBag) - - stateBag.Put(constants.ArmLocation, "Unit Test: Location") - value := "Unit Test: Tags" - tags := map[string]*string{ - "tag01": &value, - } - stateBag.Put(constants.ArmTags, tags) - - return stateBag -} diff --git a/builder/azure/arm/step_set_certificate.go b/builder/azure/arm/step_set_certificate.go deleted file mode 100644 index de8845275..000000000 --- a/builder/azure/arm/step_set_certificate.go +++ /dev/null @@ -1,37 +0,0 @@ -package arm - -import ( - "context" - - "github.com/hashicorp/packer-plugin-sdk/multistep" - packersdk "github.com/hashicorp/packer-plugin-sdk/packer" - "github.com/hashicorp/packer/builder/azure/common/constants" -) - -type StepSetCertificate struct { - config *Config - say func(message string) - error func(e error) -} - -func NewStepSetCertificate(config *Config, ui packersdk.Ui) *StepSetCertificate { - var step = &StepSetCertificate{ - config: config, - say: func(message string) { ui.Say(message) }, - error: func(e error) { ui.Error(e.Error()) }, - } - - return step -} - -func (s *StepSetCertificate) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { - s.say("Setting the certificate's URL ...") - - var winRMCertificateUrl = state.Get(constants.ArmCertificateUrl).(string) - s.config.tmpWinRMCertificateUrl = winRMCertificateUrl - - return multistep.ActionContinue -} - -func (*StepSetCertificate) Cleanup(multistep.StateBag) { -} diff --git a/builder/azure/arm/step_set_certificate_test.go b/builder/azure/arm/step_set_certificate_test.go deleted file mode 100644 index 59c0a0fc1..000000000 --- a/builder/azure/arm/step_set_certificate_test.go +++ /dev/null @@ -1,54 +0,0 @@ -package arm - -import ( - "context" - "testing" - - "github.com/hashicorp/packer-plugin-sdk/multistep" - "github.com/hashicorp/packer/builder/azure/common/constants" -) - -func TestStepSetCertificateShouldPassIfGetPasses(t *testing.T) { - var testSubject = &StepSetCertificate{ - config: new(Config), - say: func(message string) {}, - error: func(e error) {}, - } - - stateBag := createTestStateBagStepSetCertificate() - - var result = testSubject.Run(context.Background(), stateBag) - if result != multistep.ActionContinue { - t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result) - } - - if _, ok := stateBag.GetOk(constants.Error); ok == true { - t.Fatalf("Expected the step to not set stateBag['%s'], but it was.", constants.Error) - } -} - -func TestStepSetCertificateShouldTakeStepArgumentsFromStateBag(t *testing.T) { - config := new(Config) - var testSubject = &StepSetCertificate{ - config: config, - say: func(message string) {}, - error: func(e error) {}, - } - - stateBag := createTestStateBagStepSetCertificate() - var result = testSubject.Run(context.Background(), stateBag) - - if result != multistep.ActionContinue { - t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result) - } - - if config.tmpWinRMCertificateUrl != stateBag.Get(constants.ArmCertificateUrl) { - t.Fatalf("Expected config.tmpWinRMCertificateUrl to be %s, but got %s'", stateBag.Get(constants.ArmCertificateUrl), config.tmpWinRMCertificateUrl) - } -} - -func createTestStateBagStepSetCertificate() multistep.StateBag { - stateBag := new(multistep.BasicStateBag) - stateBag.Put(constants.ArmCertificateUrl, "Unit Test: Certificate URL") - return stateBag -} diff --git a/builder/azure/arm/step_snapshot_data_disks.go b/builder/azure/arm/step_snapshot_data_disks.go deleted file mode 100644 index a18e20e24..000000000 --- a/builder/azure/arm/step_snapshot_data_disks.go +++ /dev/null @@ -1,108 +0,0 @@ -package arm - -import ( - "context" - "fmt" - "strconv" - - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2018-04-01/compute" - "github.com/Azure/go-autorest/autorest/to" - "github.com/hashicorp/packer-plugin-sdk/multistep" - packersdk "github.com/hashicorp/packer-plugin-sdk/packer" - "github.com/hashicorp/packer/builder/azure/common/constants" -) - -type StepSnapshotDataDisks struct { - client *AzureClient - create func(ctx context.Context, resourceGroupName string, srcUriVhd string, location string, tags map[string]*string, dstSnapshotName string) error - say func(message string) - error func(e error) - enable func() bool -} - -func NewStepSnapshotDataDisks(client *AzureClient, ui packersdk.Ui, config *Config) *StepSnapshotDataDisks { - var step = &StepSnapshotDataDisks{ - client: client, - say: func(message string) { ui.Say(message) }, - error: func(e error) { ui.Error(e.Error()) }, - enable: func() bool { return config.isManagedImage() && config.ManagedImageDataDiskSnapshotPrefix != "" }, - } - - step.create = step.createDataDiskSnapshot - return step -} - -func (s *StepSnapshotDataDisks) createDataDiskSnapshot(ctx context.Context, resourceGroupName string, srcUriVhd string, location string, tags map[string]*string, dstSnapshotName string) error { - - srcVhdToSnapshot := compute.Snapshot{ - DiskProperties: &compute.DiskProperties{ - CreationData: &compute.CreationData{ - CreateOption: compute.Copy, - SourceResourceID: to.StringPtr(srcUriVhd), - }, - }, - Location: to.StringPtr(location), - Tags: tags, - } - - f, err := s.client.SnapshotsClient.CreateOrUpdate(ctx, resourceGroupName, dstSnapshotName, srcVhdToSnapshot) - - if err != nil { - s.say(s.client.LastError.Error()) - return err - } - - err = f.WaitForCompletionRef(ctx, s.client.SnapshotsClient.Client) - - if err != nil { - s.say(s.client.LastError.Error()) - return err - } - - createdSnapshot, err := f.Result(s.client.SnapshotsClient) - - if err != nil { - s.say(s.client.LastError.Error()) - return err - } - - s.say(fmt.Sprintf(" -> Snapshot ID : '%s'", *(createdSnapshot.ID))) - return nil -} - -func (s *StepSnapshotDataDisks) Run(ctx context.Context, stateBag multistep.StateBag) multistep.StepAction { - if !s.enable() { - return multistep.ActionContinue - } - - var resourceGroupName = stateBag.Get(constants.ArmManagedImageResourceGroupName).(string) - var location = stateBag.Get(constants.ArmLocation).(string) - var tags = stateBag.Get(constants.ArmTags).(map[string]*string) - var additionalDisks = stateBag.Get(constants.ArmAdditionalDiskVhds).([]string) - var dstSnapshotPrefix = stateBag.Get(constants.ArmManagedImageDataDiskSnapshotPrefix).(string) - - if len(additionalDisks) == 1 { - s.say(fmt.Sprintf("Snapshotting data disk ...")) - } else { - s.say(fmt.Sprintf("Snapshotting data disks ...")) - } - - for i, disk := range additionalDisks { - s.say(fmt.Sprintf(" -> Data Disk : '%s'", disk)) - - dstSnapshotName := dstSnapshotPrefix + strconv.Itoa(i) - err := s.create(ctx, resourceGroupName, disk, location, tags, dstSnapshotName) - - if err != nil { - stateBag.Put(constants.Error, err) - s.error(err) - - return multistep.ActionHalt - } - } - - return multistep.ActionContinue -} - -func (*StepSnapshotDataDisks) Cleanup(multistep.StateBag) { -} diff --git a/builder/azure/arm/step_snapshot_data_disks_test.go b/builder/azure/arm/step_snapshot_data_disks_test.go deleted file mode 100644 index 344b24fb7..000000000 --- a/builder/azure/arm/step_snapshot_data_disks_test.go +++ /dev/null @@ -1,88 +0,0 @@ -package arm - -import ( - "context" - "fmt" - "testing" - - "github.com/hashicorp/packer-plugin-sdk/multistep" - "github.com/hashicorp/packer/builder/azure/common/constants" -) - -func TestStepSnapshotDataDisksShouldFailIfSnapshotFails(t *testing.T) { - var testSubject = &StepSnapshotDataDisks{ - create: func(context.Context, string, string, string, map[string]*string, string) error { - return fmt.Errorf("!! Unit Test FAIL !!") - }, - say: func(message string) {}, - error: func(e error) {}, - enable: func() bool { return true }, - } - - stateBag := createTestStateBagStepSnapshotDataDisks() - - var result = testSubject.Run(context.Background(), stateBag) - if result != multistep.ActionHalt { - t.Fatalf("Expected the step to return 'ActionHalt', but got '%d'.", result) - } - - if _, ok := stateBag.GetOk(constants.Error); ok == false { - t.Fatalf("Expected the step to set stateBag['%s'], but it was not.", constants.Error) - } -} - -func TestStepSnapshotDataDisksShouldNotExecute(t *testing.T) { - var testSubject = &StepSnapshotDataDisks{ - create: func(context.Context, string, string, string, map[string]*string, string) error { - return fmt.Errorf("!! Unit Test FAIL !!") - }, - say: func(message string) {}, - error: func(e error) {}, - enable: func() bool { return false }, - } - - var result = testSubject.Run(context.Background(), nil) - if result != multistep.ActionContinue { - t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result) - } -} - -func TestStepSnapshotDataDisksShouldPassIfSnapshotPasses(t *testing.T) { - var testSubject = &StepSnapshotDataDisks{ - create: func(context.Context, string, string, string, map[string]*string, string) error { - return nil - }, - say: func(message string) {}, - error: func(e error) {}, - enable: func() bool { return true }, - } - - stateBag := createTestStateBagStepSnapshotDataDisks() - - var result = testSubject.Run(context.Background(), stateBag) - if result != multistep.ActionContinue { - t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result) - } - - if _, ok := stateBag.GetOk(constants.Error); ok == true { - t.Fatalf("Expected the step to not set stateBag['%s'], but it was.", constants.Error) - } -} - -func createTestStateBagStepSnapshotDataDisks() multistep.StateBag { - stateBag := new(multistep.BasicStateBag) - - stateBag.Put(constants.ArmManagedImageResourceGroupName, "Unit Test: ResourceGroupName") - stateBag.Put(constants.ArmLocation, "Unit Test: Location") - - value := "Unit Test: Tags" - tags := map[string]*string{ - "tag01": &value, - } - stateBag.Put(constants.ArmTags, tags) - - stateBag.Put(constants.ArmAdditionalDiskVhds, []string{"subscriptions/123-456-789/resourceGroups/existingresourcegroup/providers/Microsoft.Compute/disks/osdisk"}) - stateBag.Put(constants.ArmManagedImageDataDiskSnapshotPrefix, "Unit Test: ManagedImageDataDiskSnapshotPrefix") - - return stateBag -} diff --git a/builder/azure/arm/step_snapshot_os_disk.go b/builder/azure/arm/step_snapshot_os_disk.go deleted file mode 100644 index 03c4090ed..000000000 --- a/builder/azure/arm/step_snapshot_os_disk.go +++ /dev/null @@ -1,99 +0,0 @@ -package arm - -import ( - "context" - "fmt" - - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2018-04-01/compute" - "github.com/Azure/go-autorest/autorest/to" - "github.com/hashicorp/packer-plugin-sdk/multistep" - packersdk "github.com/hashicorp/packer-plugin-sdk/packer" - "github.com/hashicorp/packer/builder/azure/common/constants" -) - -type StepSnapshotOSDisk struct { - client *AzureClient - create func(ctx context.Context, resourceGroupName string, srcUriVhd string, location string, tags map[string]*string, dstSnapshotName string) error - say func(message string) - error func(e error) - enable func() bool -} - -func NewStepSnapshotOSDisk(client *AzureClient, ui packersdk.Ui, config *Config) *StepSnapshotOSDisk { - var step = &StepSnapshotOSDisk{ - client: client, - say: func(message string) { ui.Say(message) }, - error: func(e error) { ui.Error(e.Error()) }, - enable: func() bool { return config.isManagedImage() && config.ManagedImageOSDiskSnapshotName != "" }, - } - - step.create = step.createSnapshot - return step -} - -func (s *StepSnapshotOSDisk) createSnapshot(ctx context.Context, resourceGroupName string, srcUriVhd string, location string, tags map[string]*string, dstSnapshotName string) error { - - srcVhdToSnapshot := compute.Snapshot{ - DiskProperties: &compute.DiskProperties{ - CreationData: &compute.CreationData{ - CreateOption: compute.Copy, - SourceResourceID: to.StringPtr(srcUriVhd), - }, - }, - Location: to.StringPtr(location), - Tags: tags, - } - - f, err := s.client.SnapshotsClient.CreateOrUpdate(ctx, resourceGroupName, dstSnapshotName, srcVhdToSnapshot) - - if err != nil { - s.say(s.client.LastError.Error()) - return err - } - - err = f.WaitForCompletionRef(ctx, s.client.SnapshotsClient.Client) - - if err != nil { - s.say(s.client.LastError.Error()) - return err - } - - createdSnapshot, err := f.Result(s.client.SnapshotsClient) - - if err != nil { - s.say(s.client.LastError.Error()) - return err - } - - s.say(fmt.Sprintf(" -> Snapshot ID : '%s'", *(createdSnapshot.ID))) - return nil -} - -func (s *StepSnapshotOSDisk) Run(ctx context.Context, stateBag multistep.StateBag) multistep.StepAction { - if !s.enable() { - return multistep.ActionContinue - } - - s.say("Snapshotting OS disk ...") - - var resourceGroupName = stateBag.Get(constants.ArmManagedImageResourceGroupName).(string) - var location = stateBag.Get(constants.ArmLocation).(string) - var tags = stateBag.Get(constants.ArmTags).(map[string]*string) - var srcUriVhd = stateBag.Get(constants.ArmOSDiskVhd).(string) - var dstSnapshotName = stateBag.Get(constants.ArmManagedImageOSDiskSnapshotName).(string) - - s.say(fmt.Sprintf(" -> OS Disk : '%s'", srcUriVhd)) - err := s.create(ctx, resourceGroupName, srcUriVhd, location, tags, dstSnapshotName) - - if err != nil { - stateBag.Put(constants.Error, err) - s.error(err) - - return multistep.ActionHalt - } - - return multistep.ActionContinue -} - -func (*StepSnapshotOSDisk) Cleanup(multistep.StateBag) { -} diff --git a/builder/azure/arm/step_snapshot_os_disk_test.go b/builder/azure/arm/step_snapshot_os_disk_test.go deleted file mode 100644 index 9cbcd89e0..000000000 --- a/builder/azure/arm/step_snapshot_os_disk_test.go +++ /dev/null @@ -1,89 +0,0 @@ -package arm - -import ( - "context" - "fmt" - "testing" - - "github.com/hashicorp/packer-plugin-sdk/multistep" - "github.com/hashicorp/packer/builder/azure/common/constants" -) - -func TestStepSnapshotOSDiskShouldFailIfSnapshotFails(t *testing.T) { - var testSubject = &StepSnapshotOSDisk{ - create: func(context.Context, string, string, string, map[string]*string, string) error { - return fmt.Errorf("!! Unit Test FAIL !!") - }, - say: func(message string) {}, - error: func(e error) {}, - enable: func() bool { return true }, - } - - stateBag := createTestStateBagStepSnapshotOSDisk() - - var result = testSubject.Run(context.Background(), stateBag) - if result != multistep.ActionHalt { - t.Fatalf("Expected the step to return 'ActionHalt', but got '%d'.", result) - } - - if _, ok := stateBag.GetOk(constants.Error); ok == false { - t.Fatalf("Expected the step to set stateBag['%s'], but it was not.", constants.Error) - } -} - -func TestStepSnapshotOSDiskShouldNotExecute(t *testing.T) { - var testSubject = &StepSnapshotOSDisk{ - create: func(context.Context, string, string, string, map[string]*string, string) error { - return fmt.Errorf("!! Unit Test FAIL !!") - }, - say: func(message string) {}, - error: func(e error) {}, - enable: func() bool { return false }, - } - - var result = testSubject.Run(context.Background(), nil) - if result != multistep.ActionContinue { - t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result) - } -} - -func TestStepSnapshotOSDiskShouldPassIfSnapshotPasses(t *testing.T) { - var testSubject = &StepSnapshotOSDisk{ - create: func(context.Context, string, string, string, map[string]*string, string) error { - return nil - }, - say: func(message string) {}, - error: func(e error) {}, - enable: func() bool { return true }, - } - - stateBag := createTestStateBagStepSnapshotOSDisk() - - var result = testSubject.Run(context.Background(), stateBag) - if result != multistep.ActionContinue { - t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result) - } - - if _, ok := stateBag.GetOk(constants.Error); ok == true { - t.Fatalf("Expected the step to not set stateBag['%s'], but it was.", constants.Error) - } -} - -func createTestStateBagStepSnapshotOSDisk() multistep.StateBag { - stateBag := new(multistep.BasicStateBag) - - stateBag.Put(constants.ArmManagedImageResourceGroupName, "Unit Test: ResourceGroupName") - stateBag.Put(constants.ArmLocation, "Unit Test: Location") - - value := "Unit Test: Tags" - tags := map[string]*string{ - "tag01": &value, - } - - stateBag.Put(constants.ArmTags, tags) - - stateBag.Put(constants.ArmOSDiskVhd, "subscriptions/123-456-789/resourceGroups/existingresourcegroup/providers/Microsoft.Compute/disks/osdisk") - stateBag.Put(constants.ArmManagedImageOSDiskSnapshotName, "Unit Test: ManagedImageOSDiskSnapshotName") - - return stateBag -} diff --git a/builder/azure/arm/step_test.go b/builder/azure/arm/step_test.go deleted file mode 100644 index 6d53cf957..000000000 --- a/builder/azure/arm/step_test.go +++ /dev/null @@ -1,40 +0,0 @@ -package arm - -import ( - "fmt" - "testing" - - "github.com/hashicorp/packer-plugin-sdk/multistep" - "github.com/hashicorp/packer/builder/azure/common/constants" -) - -func TestProcessStepResultShouldContinueForNonErrors(t *testing.T) { - stateBag := new(multistep.BasicStateBag) - - code := processStepResult(nil, func(error) { t.Fatal("Should not be called!") }, stateBag) - if _, ok := stateBag.GetOk(constants.Error); ok { - t.Errorf("Error was nil, but was still in the state bag.") - } - - if code != multistep.ActionContinue { - t.Errorf("Expected ActionContinue(%d), but got=%d", multistep.ActionContinue, code) - } -} - -func TestProcessStepResultShouldHaltOnError(t *testing.T) { - stateBag := new(multistep.BasicStateBag) - isSaidError := false - - code := processStepResult(fmt.Errorf("boom"), func(error) { isSaidError = true }, stateBag) - if _, ok := stateBag.GetOk(constants.Error); !ok { - t.Errorf("Error was non nil, but was not in the state bag.") - } - - if !isSaidError { - t.Errorf("Expected error to be said, but it was not.") - } - - if code != multistep.ActionHalt { - t.Errorf("Expected ActionHalt(%d), but got=%d", multistep.ActionHalt, code) - } -} diff --git a/builder/azure/arm/step_validate_template.go b/builder/azure/arm/step_validate_template.go deleted file mode 100644 index 415f1ff36..000000000 --- a/builder/azure/arm/step_validate_template.go +++ /dev/null @@ -1,61 +0,0 @@ -package arm - -import ( - "context" - "fmt" - - "github.com/hashicorp/packer-plugin-sdk/multistep" - packersdk "github.com/hashicorp/packer-plugin-sdk/packer" - "github.com/hashicorp/packer/builder/azure/common/constants" -) - -type StepValidateTemplate struct { - client *AzureClient - validate func(ctx context.Context, resourceGroupName string, deploymentName string) error - say func(message string) - error func(e error) - config *Config - factory templateFactoryFunc -} - -func NewStepValidateTemplate(client *AzureClient, ui packersdk.Ui, config *Config, factory templateFactoryFunc) *StepValidateTemplate { - var step = &StepValidateTemplate{ - client: client, - say: func(message string) { ui.Say(message) }, - error: func(e error) { ui.Error(e.Error()) }, - config: config, - factory: factory, - } - - step.validate = step.validateTemplate - return step -} - -func (s *StepValidateTemplate) validateTemplate(ctx context.Context, resourceGroupName string, deploymentName string) error { - deployment, err := s.factory(s.config) - if err != nil { - return err - } - - _, err = s.client.DeploymentsClient.Validate(ctx, resourceGroupName, deploymentName, *deployment) - if err != nil { - s.say(s.client.LastError.Error()) - } - return err -} - -func (s *StepValidateTemplate) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { - s.say("Validating deployment template ...") - - var resourceGroupName = state.Get(constants.ArmResourceGroupName).(string) - var deploymentName = state.Get(constants.ArmDeploymentName).(string) - - s.say(fmt.Sprintf(" -> ResourceGroupName : '%s'", resourceGroupName)) - s.say(fmt.Sprintf(" -> DeploymentName : '%s'", deploymentName)) - - err := s.validate(ctx, resourceGroupName, deploymentName) - return processStepResult(err, s.error, state) -} - -func (*StepValidateTemplate) Cleanup(multistep.StateBag) { -} diff --git a/builder/azure/arm/step_validate_template_test.go b/builder/azure/arm/step_validate_template_test.go deleted file mode 100644 index 972bbd692..000000000 --- a/builder/azure/arm/step_validate_template_test.go +++ /dev/null @@ -1,91 +0,0 @@ -package arm - -import ( - "context" - "fmt" - "testing" - - "github.com/hashicorp/packer-plugin-sdk/multistep" - "github.com/hashicorp/packer/builder/azure/common/constants" -) - -func TestStepValidateTemplateShouldFailIfValidateFails(t *testing.T) { - var testSubject = &StepValidateTemplate{ - validate: func(context.Context, string, string) error { return fmt.Errorf("!! Unit Test FAIL !!") }, - say: func(message string) {}, - error: func(e error) {}, - } - - stateBag := createTestStateBagStepValidateTemplate() - - var result = testSubject.Run(context.Background(), stateBag) - if result != multistep.ActionHalt { - t.Fatalf("Expected the step to return 'ActionHalt', but got '%d'.", result) - } - - if _, ok := stateBag.GetOk(constants.Error); ok == false { - t.Fatalf("Expected the step to set stateBag['%s'], but it was not.", constants.Error) - } -} - -func TestStepValidateTemplateShouldPassIfValidatePasses(t *testing.T) { - var testSubject = &StepValidateTemplate{ - validate: func(context.Context, string, string) error { return nil }, - say: func(message string) {}, - error: func(e error) {}, - } - - stateBag := createTestStateBagStepValidateTemplate() - - var result = testSubject.Run(context.Background(), stateBag) - if result != multistep.ActionContinue { - t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result) - } - - if _, ok := stateBag.GetOk(constants.Error); ok == true { - t.Fatalf("Expected the step to not set stateBag['%s'], but it was.", constants.Error) - } -} - -func TestStepValidateTemplateShouldTakeStepArgumentsFromStateBag(t *testing.T) { - var actualResourceGroupName string - var actualDeploymentName string - - var testSubject = &StepValidateTemplate{ - validate: func(ctx context.Context, resourceGroupName string, deploymentName string) error { - actualResourceGroupName = resourceGroupName - actualDeploymentName = deploymentName - - return nil - }, - say: func(message string) {}, - error: func(e error) {}, - } - - stateBag := createTestStateBagStepValidateTemplate() - var result = testSubject.Run(context.Background(), stateBag) - - if result != multistep.ActionContinue { - t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result) - } - - var expectedDeploymentName = stateBag.Get(constants.ArmDeploymentName).(string) - var expectedResourceGroupName = stateBag.Get(constants.ArmResourceGroupName).(string) - - if actualDeploymentName != expectedDeploymentName { - t.Fatal("Expected the step to source 'constants.ArmDeploymentName' from the state bag, but it did not.") - } - - if actualResourceGroupName != expectedResourceGroupName { - t.Fatal("Expected the step to source 'constants.ArmResourceGroupName' from the state bag, but it did not.") - } -} - -func createTestStateBagStepValidateTemplate() multistep.StateBag { - stateBag := new(multistep.BasicStateBag) - - stateBag.Put(constants.ArmDeploymentName, "Unit Test: DeploymentName") - stateBag.Put(constants.ArmResourceGroupName, "Unit Test: ResourceGroupName") - - return stateBag -} diff --git a/builder/azure/arm/template_factory.go b/builder/azure/arm/template_factory.go deleted file mode 100644 index e8ee00f0b..000000000 --- a/builder/azure/arm/template_factory.go +++ /dev/null @@ -1,213 +0,0 @@ -package arm - -import ( - "encoding/json" - - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2018-04-01/compute" - "github.com/Azure/azure-sdk-for-go/services/resources/mgmt/2018-02-01/resources" - - "fmt" - - "github.com/hashicorp/packer/builder/azure/common/constants" - "github.com/hashicorp/packer/builder/azure/common/template" -) - -type templateFactoryFunc func(*Config) (*resources.Deployment, error) - -func GetKeyVaultDeployment(config *Config) (*resources.Deployment, error) { - params := &template.TemplateParameters{ - KeyVaultName: &template.TemplateParameter{Value: config.tmpKeyVaultName}, - KeyVaultSKU: &template.TemplateParameter{Value: config.BuildKeyVaultSKU}, - KeyVaultSecretValue: &template.TemplateParameter{Value: config.winrmCertificate}, - ObjectId: &template.TemplateParameter{Value: config.ClientConfig.ObjectID}, - TenantId: &template.TemplateParameter{Value: config.ClientConfig.TenantID}, - } - - builder, _ := template.NewTemplateBuilder(template.KeyVault) - builder.SetTags(&config.AzureTags) - - doc, _ := builder.ToJSON() - return createDeploymentParameters(*doc, params) -} - -func GetVirtualMachineDeployment(config *Config) (*resources.Deployment, error) { - params := &template.TemplateParameters{ - AdminUsername: &template.TemplateParameter{Value: config.UserName}, - AdminPassword: &template.TemplateParameter{Value: config.Password}, - DnsNameForPublicIP: &template.TemplateParameter{Value: config.tmpComputeName}, - NicName: &template.TemplateParameter{Value: config.tmpNicName}, - OSDiskName: &template.TemplateParameter{Value: config.tmpOSDiskName}, - DataDiskName: &template.TemplateParameter{Value: config.tmpDataDiskName}, - PublicIPAddressName: &template.TemplateParameter{Value: config.tmpPublicIPAddressName}, - SubnetName: &template.TemplateParameter{Value: config.tmpSubnetName}, - StorageAccountBlobEndpoint: &template.TemplateParameter{Value: config.storageAccountBlobEndpoint}, - VirtualNetworkName: &template.TemplateParameter{Value: config.tmpVirtualNetworkName}, - NsgName: &template.TemplateParameter{Value: config.tmpNsgName}, - VMSize: &template.TemplateParameter{Value: config.VMSize}, - VMName: &template.TemplateParameter{Value: config.tmpComputeName}, - } - - builder, err := template.NewTemplateBuilder(template.BasicTemplate) - if err != nil { - return nil, err - } - osType := compute.Linux - - switch config.OSType { - case constants.Target_Linux: - err = builder.BuildLinux(config.sshAuthorizedKey, config.Comm.SSHPassword == "") // if ssh password is not explicitly specified, disable password auth - if err != nil { - return nil, err - } - case constants.Target_Windows: - osType = compute.Windows - err = builder.BuildWindows(config.tmpKeyVaultName, config.tmpWinRMCertificateUrl) - if err != nil { - return nil, err - } - } - - if len(config.UserAssignedManagedIdentities) != 0 { - if err := builder.SetIdentity(config.UserAssignedManagedIdentities); err != nil { - return nil, err - } - } - - if config.ImageUrl != "" { - err = builder.SetImageUrl(config.ImageUrl, osType, config.diskCachingType) - if err != nil { - return nil, err - } - } else if config.CustomManagedImageName != "" { - err = builder.SetManagedDiskUrl(config.customManagedImageID, config.managedImageStorageAccountType, config.diskCachingType) - if err != nil { - return nil, err - } - } else if config.ManagedImageName != "" && config.ImagePublisher != "" { - imageID := fmt.Sprintf("/subscriptions/%s/providers/Microsoft.Compute/locations/%s/publishers/%s/ArtifactTypes/vmimage/offers/%s/skus/%s/versions/%s", - config.ClientConfig.SubscriptionID, - config.Location, - config.ImagePublisher, - config.ImageOffer, - config.ImageSku, - config.ImageVersion) - - builder.SetManagedMarketplaceImage(config.Location, config.ImagePublisher, config.ImageOffer, config.ImageSku, config.ImageVersion, imageID, config.managedImageStorageAccountType, config.diskCachingType) - } else if config.SharedGallery.Subscription != "" { - imageID := fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Compute/galleries/%s/images/%s", - config.SharedGallery.Subscription, - config.SharedGallery.ResourceGroup, - config.SharedGallery.GalleryName, - config.SharedGallery.ImageName) - if config.SharedGallery.ImageVersion != "" { - imageID += fmt.Sprintf("/versions/%s", - config.SharedGallery.ImageVersion) - } - - err = builder.SetSharedGalleryImage(config.Location, imageID, config.diskCachingType) - if err != nil { - return nil, err - } - } else { - err = builder.SetMarketPlaceImage(config.ImagePublisher, config.ImageOffer, config.ImageSku, config.ImageVersion, config.diskCachingType) - if err != nil { - return nil, err - } - } - - if config.OSDiskSizeGB > 0 { - err = builder.SetOSDiskSizeGB(config.OSDiskSizeGB) - if err != nil { - return nil, err - } - } - - if len(config.AdditionalDiskSize) > 0 { - isManaged := config.CustomManagedImageName != "" || (config.ManagedImageName != "" && config.ImagePublisher != "") || config.SharedGallery.Subscription != "" - err = builder.SetAdditionalDisks(config.AdditionalDiskSize, config.tmpDataDiskName, isManaged, config.diskCachingType) - if err != nil { - return nil, err - } - } - - if config.customData != "" { - err = builder.SetCustomData(config.customData) - if err != nil { - return nil, err - } - } - - if config.PlanInfo.PlanName != "" { - err = builder.SetPlanInfo(config.PlanInfo.PlanName, config.PlanInfo.PlanProduct, config.PlanInfo.PlanPublisher, config.PlanInfo.PlanPromotionCode) - if err != nil { - return nil, err - } - } - - if config.VirtualNetworkName != "" && DefaultPrivateVirtualNetworkWithPublicIp != config.PrivateVirtualNetworkWithPublicIp { - err = builder.SetPrivateVirtualNetworkWithPublicIp( - config.VirtualNetworkResourceGroupName, - config.VirtualNetworkName, - config.VirtualNetworkSubnetName) - if err != nil { - return nil, err - } - } else if config.VirtualNetworkName != "" { - err = builder.SetVirtualNetwork( - config.VirtualNetworkResourceGroupName, - config.VirtualNetworkName, - config.VirtualNetworkSubnetName) - if err != nil { - return nil, err - } - } - - if config.AllowedInboundIpAddresses != nil && len(config.AllowedInboundIpAddresses) >= 1 && config.Comm.Port() != 0 { - err = builder.SetNetworkSecurityGroup(config.AllowedInboundIpAddresses, config.Comm.Port()) - if err != nil { - return nil, err - } - } - - if config.BootDiagSTGAccount != "" { - err = builder.SetBootDiagnostics(config.BootDiagSTGAccount) - if err != nil { - return nil, err - } - } - - err = builder.SetTags(&config.AzureTags) - if err != nil { - return nil, err - } - - doc, _ := builder.ToJSON() - return createDeploymentParameters(*doc, params) -} - -func createDeploymentParameters(doc string, parameters *template.TemplateParameters) (*resources.Deployment, error) { - var template map[string]interface{} - err := json.Unmarshal(([]byte)(doc), &template) - if err != nil { - return nil, err - } - - bs, err := json.Marshal(*parameters) - if err != nil { - return nil, err - } - - var templateParameters map[string]interface{} - err = json.Unmarshal(bs, &templateParameters) - if err != nil { - return nil, err - } - - return &resources.Deployment{ - Properties: &resources.DeploymentProperties{ - Mode: resources.Incremental, - Template: &template, - Parameters: &templateParameters, - }, - }, nil -} diff --git a/builder/azure/arm/template_factory_test.TestKeyVaultDeployment03.approved.json b/builder/azure/arm/template_factory_test.TestKeyVaultDeployment03.approved.json deleted file mode 100644 index c4f85c6a8..000000000 --- a/builder/azure/arm/template_factory_test.TestKeyVaultDeployment03.approved.json +++ /dev/null @@ -1,76 +0,0 @@ -{ - "$schema": "http://schema.management.azure.com/schemas/2014-04-01-preview/deploymentTemplate.json", - "contentVersion": "1.0.0.0", - "parameters": { - "keyVaultName": { - "type": "string" - }, - "keyVaultSKU": { - "type": "string" - }, - "keyVaultSecretValue": { - "type": "securestring" - }, - "objectId": { - "type": "string" - }, - "tenantId": { - "type": "string" - } - }, - "resources": [ - { - "apiVersion": "[variables('apiVersion')]", - "location": "[variables('location')]", - "name": "[parameters('keyVaultName')]", - "properties": { - "accessPolicies": [ - { - "objectId": "[parameters('objectId')]", - "permissions": { - "keys": [ - "all" - ], - "secrets": [ - "all" - ] - }, - "tenantId": "[parameters('tenantId')]" - } - ], - "enableSoftDelete": "true", - "enabledForDeployment": "true", - "enabledForTemplateDeployment": "true", - "sku": { - "family": "A", - "name": "[parameters('keyVaultSKU')]" - }, - "tenantId": "[parameters('tenantId')]" - }, - "resources": [ - { - "apiVersion": "[variables('apiVersion')]", - "dependsOn": [ - "[concat('Microsoft.KeyVault/vaults/', parameters('keyVaultName'))]" - ], - "name": "[variables('keyVaultSecretName')]", - "properties": { - "value": "[parameters('keyVaultSecretValue')]" - }, - "type": "secrets" - } - ], - "tags": { - "tag01": "value01", - "tag02": "value02", - "tag03": "value03" - }, - "type": "Microsoft.KeyVault/vaults" - } - ], - "variables": { - "apiVersion": "2015-06-01", - "keyVaultSecretName": "packerKeyVaultSecret", - "location": "[resourceGroup().location]" - } -} \ No newline at end of file diff --git a/builder/azure/arm/template_factory_test.TestPlanInfo01.approved.json b/builder/azure/arm/template_factory_test.TestPlanInfo01.approved.json deleted file mode 100644 index 1b9d40d93..000000000 --- a/builder/azure/arm/template_factory_test.TestPlanInfo01.approved.json +++ /dev/null @@ -1,211 +0,0 @@ -{ - "$schema": "http://schema.management.azure.com/schemas/2014-04-01-preview/deploymentTemplate.json", - "contentVersion": "1.0.0.0", - "parameters": { - "adminPassword": { - "type": "string" - }, - "adminUsername": { - "type": "string" - }, - "dataDiskName": { - "type": "string" - }, - "dnsNameForPublicIP": { - "type": "string" - }, - "nicName": { - "type": "string" - }, - "nsgName": { - "type": "string" - }, - "osDiskName": { - "type": "string" - }, - "publicIPAddressName": { - "type": "string" - }, - "storageAccountBlobEndpoint": { - "type": "string" - }, - "subnetName": { - "type": "string" - }, - "virtualNetworkName": { - "type": "string" - }, - "vmName": { - "type": "string" - }, - "vmSize": { - "type": "string" - } - }, - "resources": [ - { - "apiVersion": "[variables('publicIPAddressApiVersion')]", - "location": "[variables('location')]", - "name": "[parameters('publicIPAddressName')]", - "properties": { - "dnsSettings": { - "domainNameLabel": "[parameters('dnsNameForPublicIP')]" - }, - "publicIPAllocationMethod": "[variables('publicIPAddressType')]" - }, - "tags": { - "PlanInfo": "planName00", - "PlanProduct": "planProduct00", - "PlanPromotionCode": "", - "PlanPublisher": "planPublisher00" - }, - "type": "Microsoft.Network/publicIPAddresses" - }, - { - "apiVersion": "[variables('virtualNetworksApiVersion')]", - "location": "[variables('location')]", - "name": "[variables('virtualNetworkName')]", - "properties": { - "addressSpace": { - "addressPrefixes": [ - "[variables('addressPrefix')]" - ] - }, - "subnets": [ - { - "name": "[variables('subnetName')]", - "properties": { - "addressPrefix": "[variables('subnetAddressPrefix')]" - } - } - ] - }, - "tags": { - "PlanInfo": "planName00", - "PlanProduct": "planProduct00", - "PlanPromotionCode": "", - "PlanPublisher": "planPublisher00" - }, - "type": "Microsoft.Network/virtualNetworks" - }, - { - "apiVersion": "[variables('networkInterfacesApiVersion')]", - "dependsOn": [ - "[concat('Microsoft.Network/publicIPAddresses/', parameters('publicIPAddressName'))]", - "[concat('Microsoft.Network/virtualNetworks/', variables('virtualNetworkName'))]" - ], - "location": "[variables('location')]", - "name": "[parameters('nicName')]", - "properties": { - "ipConfigurations": [ - { - "name": "ipconfig", - "properties": { - "privateIPAllocationMethod": "Dynamic", - "publicIPAddress": { - "id": "[resourceId('Microsoft.Network/publicIPAddresses', parameters('publicIPAddressName'))]" - }, - "subnet": { - "id": "[variables('subnetRef')]" - } - } - } - ] - }, - "tags": { - "PlanInfo": "planName00", - "PlanProduct": "planProduct00", - "PlanPromotionCode": "", - "PlanPublisher": "planPublisher00" - }, - "type": "Microsoft.Network/networkInterfaces" - }, - { - "apiVersion": "[variables('apiVersion')]", - "dependsOn": [ - "[concat('Microsoft.Network/networkInterfaces/', parameters('nicName'))]" - ], - "location": "[variables('location')]", - "name": "[parameters('vmName')]", - "plan": { - "name": "planName00", - "product": "planProduct00", - "publisher": "planPublisher00" - }, - "properties": { - "diagnosticsProfile": { - "bootDiagnostics": { - "enabled": false - } - }, - "hardwareProfile": { - "vmSize": "[parameters('vmSize')]" - }, - "networkProfile": { - "networkInterfaces": [ - { - "id": "[resourceId('Microsoft.Network/networkInterfaces', parameters('nicName'))]" - } - ] - }, - "osProfile": { - "adminUsername": "[parameters('adminUsername')]", - "computerName": "[parameters('vmName')]", - "linuxConfiguration": { - "disablePasswordAuthentication": true, - "ssh": { - "publicKeys": [ - { - "keyData": "", - "path": "[variables('sshKeyPath')]" - } - ] - } - } - }, - "storageProfile": { - "imageReference": { - "offer": "ignored00", - "publisher": "ignored00", - "sku": "ignored00", - "version": "latest" - }, - "osDisk": { - "caching": "ReadWrite", - "createOption": "FromImage", - "name": "[parameters('osDiskName')]", - "vhd": { - "uri": "[concat(parameters('storageAccountBlobEndpoint'),variables('vmStorageAccountContainerName'),'/', parameters('osDiskName'),'.vhd')]" - } - } - } - }, - "tags": { - "PlanInfo": "planName00", - "PlanProduct": "planProduct00", - "PlanPromotionCode": "", - "PlanPublisher": "planPublisher00" - }, - "type": "Microsoft.Compute/virtualMachines" - } - ], - "variables": { - "addressPrefix": "10.0.0.0/16", - "apiVersion": "2017-03-30", - "location": "[resourceGroup().location]", - "managedDiskApiVersion": "2017-03-30", - "networkInterfacesApiVersion": "2017-04-01", - "networkSecurityGroupsApiVersion": "2019-04-01", - "publicIPAddressApiVersion": "2017-04-01", - "publicIPAddressType": "Dynamic", - "sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]", - "subnetAddressPrefix": "10.0.0.0/24", - "subnetName": "[parameters('subnetName')]", - "subnetRef": "[concat(variables('vnetID'),'/subnets/',variables('subnetName'))]", - "virtualNetworkName": "[parameters('virtualNetworkName')]", - "virtualNetworkResourceGroup": "[resourceGroup().name]", - "virtualNetworksApiVersion": "2017-04-01", - "vmStorageAccountContainerName": "images", - "vnetID": "[resourceId(variables('virtualNetworkResourceGroup'), 'Microsoft.Network/virtualNetworks', variables('virtualNetworkName'))]" - } -} \ No newline at end of file diff --git a/builder/azure/arm/template_factory_test.TestPlanInfo02.approved.json b/builder/azure/arm/template_factory_test.TestPlanInfo02.approved.json deleted file mode 100644 index 20eda3180..000000000 --- a/builder/azure/arm/template_factory_test.TestPlanInfo02.approved.json +++ /dev/null @@ -1,216 +0,0 @@ -{ - "$schema": "http://schema.management.azure.com/schemas/2014-04-01-preview/deploymentTemplate.json", - "contentVersion": "1.0.0.0", - "parameters": { - "adminPassword": { - "type": "string" - }, - "adminUsername": { - "type": "string" - }, - "dataDiskName": { - "type": "string" - }, - "dnsNameForPublicIP": { - "type": "string" - }, - "nicName": { - "type": "string" - }, - "nsgName": { - "type": "string" - }, - "osDiskName": { - "type": "string" - }, - "publicIPAddressName": { - "type": "string" - }, - "storageAccountBlobEndpoint": { - "type": "string" - }, - "subnetName": { - "type": "string" - }, - "virtualNetworkName": { - "type": "string" - }, - "vmName": { - "type": "string" - }, - "vmSize": { - "type": "string" - } - }, - "resources": [ - { - "apiVersion": "[variables('publicIPAddressApiVersion')]", - "location": "[variables('location')]", - "name": "[parameters('publicIPAddressName')]", - "properties": { - "dnsSettings": { - "domainNameLabel": "[parameters('dnsNameForPublicIP')]" - }, - "publicIPAllocationMethod": "[variables('publicIPAddressType')]" - }, - "tags": { - "PlanInfo": "planName00", - "PlanProduct": "planProduct00", - "PlanPromotionCode": "planPromotionCode00", - "PlanPublisher": "planPublisher00", - "dept": "engineering" - }, - "type": "Microsoft.Network/publicIPAddresses" - }, - { - "apiVersion": "[variables('virtualNetworksApiVersion')]", - "location": "[variables('location')]", - "name": "[variables('virtualNetworkName')]", - "properties": { - "addressSpace": { - "addressPrefixes": [ - "[variables('addressPrefix')]" - ] - }, - "subnets": [ - { - "name": "[variables('subnetName')]", - "properties": { - "addressPrefix": "[variables('subnetAddressPrefix')]" - } - } - ] - }, - "tags": { - "PlanInfo": "planName00", - "PlanProduct": "planProduct00", - "PlanPromotionCode": "planPromotionCode00", - "PlanPublisher": "planPublisher00", - "dept": "engineering" - }, - "type": "Microsoft.Network/virtualNetworks" - }, - { - "apiVersion": "[variables('networkInterfacesApiVersion')]", - "dependsOn": [ - "[concat('Microsoft.Network/publicIPAddresses/', parameters('publicIPAddressName'))]", - "[concat('Microsoft.Network/virtualNetworks/', variables('virtualNetworkName'))]" - ], - "location": "[variables('location')]", - "name": "[parameters('nicName')]", - "properties": { - "ipConfigurations": [ - { - "name": "ipconfig", - "properties": { - "privateIPAllocationMethod": "Dynamic", - "publicIPAddress": { - "id": "[resourceId('Microsoft.Network/publicIPAddresses', parameters('publicIPAddressName'))]" - }, - "subnet": { - "id": "[variables('subnetRef')]" - } - } - } - ] - }, - "tags": { - "PlanInfo": "planName00", - "PlanProduct": "planProduct00", - "PlanPromotionCode": "planPromotionCode00", - "PlanPublisher": "planPublisher00", - "dept": "engineering" - }, - "type": "Microsoft.Network/networkInterfaces" - }, - { - "apiVersion": "[variables('apiVersion')]", - "dependsOn": [ - "[concat('Microsoft.Network/networkInterfaces/', parameters('nicName'))]" - ], - "location": "[variables('location')]", - "name": "[parameters('vmName')]", - "plan": { - "name": "planName00", - "product": "planProduct00", - "promotionCode": "planPromotionCode00", - "publisher": "planPublisher00" - }, - "properties": { - "diagnosticsProfile": { - "bootDiagnostics": { - "enabled": false - } - }, - "hardwareProfile": { - "vmSize": "[parameters('vmSize')]" - }, - "networkProfile": { - "networkInterfaces": [ - { - "id": "[resourceId('Microsoft.Network/networkInterfaces', parameters('nicName'))]" - } - ] - }, - "osProfile": { - "adminUsername": "[parameters('adminUsername')]", - "computerName": "[parameters('vmName')]", - "linuxConfiguration": { - "disablePasswordAuthentication": true, - "ssh": { - "publicKeys": [ - { - "keyData": "", - "path": "[variables('sshKeyPath')]" - } - ] - } - } - }, - "storageProfile": { - "imageReference": { - "offer": "ignored00", - "publisher": "ignored00", - "sku": "ignored00", - "version": "latest" - }, - "osDisk": { - "caching": "ReadWrite", - "createOption": "FromImage", - "name": "[parameters('osDiskName')]", - "vhd": { - "uri": "[concat(parameters('storageAccountBlobEndpoint'),variables('vmStorageAccountContainerName'),'/', parameters('osDiskName'),'.vhd')]" - } - } - } - }, - "tags": { - "PlanInfo": "planName00", - "PlanProduct": "planProduct00", - "PlanPromotionCode": "planPromotionCode00", - "PlanPublisher": "planPublisher00", - "dept": "engineering" - }, - "type": "Microsoft.Compute/virtualMachines" - } - ], - "variables": { - "addressPrefix": "10.0.0.0/16", - "apiVersion": "2017-03-30", - "location": "[resourceGroup().location]", - "managedDiskApiVersion": "2017-03-30", - "networkInterfacesApiVersion": "2017-04-01", - "networkSecurityGroupsApiVersion": "2019-04-01", - "publicIPAddressApiVersion": "2017-04-01", - "publicIPAddressType": "Dynamic", - "sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]", - "subnetAddressPrefix": "10.0.0.0/24", - "subnetName": "[parameters('subnetName')]", - "subnetRef": "[concat(variables('vnetID'),'/subnets/',variables('subnetName'))]", - "virtualNetworkName": "[parameters('virtualNetworkName')]", - "virtualNetworkResourceGroup": "[resourceGroup().name]", - "virtualNetworksApiVersion": "2017-04-01", - "vmStorageAccountContainerName": "images", - "vnetID": "[resourceId(variables('virtualNetworkResourceGroup'), 'Microsoft.Network/virtualNetworks', variables('virtualNetworkName'))]" - } -} \ No newline at end of file diff --git a/builder/azure/arm/template_factory_test.TestVirtualMachineDeployment03.approved.json b/builder/azure/arm/template_factory_test.TestVirtualMachineDeployment03.approved.json deleted file mode 100644 index 1f0e43265..000000000 --- a/builder/azure/arm/template_factory_test.TestVirtualMachineDeployment03.approved.json +++ /dev/null @@ -1,182 +0,0 @@ -{ - "$schema": "http://schema.management.azure.com/schemas/2014-04-01-preview/deploymentTemplate.json", - "contentVersion": "1.0.0.0", - "parameters": { - "adminPassword": { - "type": "string" - }, - "adminUsername": { - "type": "string" - }, - "dataDiskName": { - "type": "string" - }, - "dnsNameForPublicIP": { - "type": "string" - }, - "nicName": { - "type": "string" - }, - "nsgName": { - "type": "string" - }, - "osDiskName": { - "type": "string" - }, - "publicIPAddressName": { - "type": "string" - }, - "storageAccountBlobEndpoint": { - "type": "string" - }, - "subnetName": { - "type": "string" - }, - "virtualNetworkName": { - "type": "string" - }, - "vmName": { - "type": "string" - }, - "vmSize": { - "type": "string" - } - }, - "resources": [ - { - "apiVersion": "[variables('publicIPAddressApiVersion')]", - "location": "[variables('location')]", - "name": "[parameters('publicIPAddressName')]", - "properties": { - "dnsSettings": { - "domainNameLabel": "[parameters('dnsNameForPublicIP')]" - }, - "publicIPAllocationMethod": "[variables('publicIPAddressType')]" - }, - "type": "Microsoft.Network/publicIPAddresses" - }, - { - "apiVersion": "[variables('virtualNetworksApiVersion')]", - "location": "[variables('location')]", - "name": "[variables('virtualNetworkName')]", - "properties": { - "addressSpace": { - "addressPrefixes": [ - "[variables('addressPrefix')]" - ] - }, - "subnets": [ - { - "name": "[variables('subnetName')]", - "properties": { - "addressPrefix": "[variables('subnetAddressPrefix')]" - } - } - ] - }, - "type": "Microsoft.Network/virtualNetworks" - }, - { - "apiVersion": "[variables('networkInterfacesApiVersion')]", - "dependsOn": [ - "[concat('Microsoft.Network/publicIPAddresses/', parameters('publicIPAddressName'))]", - "[concat('Microsoft.Network/virtualNetworks/', variables('virtualNetworkName'))]" - ], - "location": "[variables('location')]", - "name": "[parameters('nicName')]", - "properties": { - "ipConfigurations": [ - { - "name": "ipconfig", - "properties": { - "privateIPAllocationMethod": "Dynamic", - "publicIPAddress": { - "id": "[resourceId('Microsoft.Network/publicIPAddresses', parameters('publicIPAddressName'))]" - }, - "subnet": { - "id": "[variables('subnetRef')]" - } - } - } - ] - }, - "type": "Microsoft.Network/networkInterfaces" - }, - { - "apiVersion": "[variables('apiVersion')]", - "dependsOn": [ - "[concat('Microsoft.Network/networkInterfaces/', parameters('nicName'))]" - ], - "location": "[variables('location')]", - "name": "[parameters('vmName')]", - "properties": { - "diagnosticsProfile": { - "bootDiagnostics": { - "enabled": false - } - }, - "hardwareProfile": { - "vmSize": "[parameters('vmSize')]" - }, - "networkProfile": { - "networkInterfaces": [ - { - "id": "[resourceId('Microsoft.Network/networkInterfaces', parameters('nicName'))]" - } - ] - }, - "osProfile": { - "adminPassword": "[parameters('adminPassword')]", - "adminUsername": "[parameters('adminUsername')]", - "computerName": "[parameters('vmName')]", - "linuxConfiguration": { - "ssh": { - "publicKeys": [ - { - "keyData": "", - "path": "[variables('sshKeyPath')]" - } - ] - } - } - }, - "storageProfile": { - "imageReference": { - "offer": "ImageOffer", - "publisher": "ImagePublisher", - "sku": "ImageSku", - "version": "ImageVersion" - }, - "osDisk": { - "caching": "ReadWrite", - "createOption": "FromImage", - "name": "[parameters('osDiskName')]", - "vhd": { - "uri": "[concat(parameters('storageAccountBlobEndpoint'),variables('vmStorageAccountContainerName'),'/', parameters('osDiskName'),'.vhd')]" - } - } - } - }, - "type": "Microsoft.Compute/virtualMachines" - } - ], - "variables": { - "addressPrefix": "10.0.0.0/16", - "apiVersion": "2017-03-30", - "location": "[resourceGroup().location]", - "managedDiskApiVersion": "2017-03-30", - "networkInterfacesApiVersion": "2017-04-01", - "networkSecurityGroupsApiVersion": "2019-04-01", - "publicIPAddressApiVersion": "2017-04-01", - "publicIPAddressType": "Dynamic", - "sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]", - "subnetAddressPrefix": "10.0.0.0/24", - "subnetName": "[parameters('subnetName')]", - "subnetRef": "[concat(variables('vnetID'),'/subnets/',variables('subnetName'))]", - "virtualNetworkName": "[parameters('virtualNetworkName')]", - "virtualNetworkResourceGroup": "[resourceGroup().name]", - "virtualNetworksApiVersion": "2017-04-01", - "vmStorageAccountContainerName": "images", - "vnetID": "[resourceId(variables('virtualNetworkResourceGroup'), 'Microsoft.Network/virtualNetworks', variables('virtualNetworkName'))]" - } -} \ No newline at end of file diff --git a/builder/azure/arm/template_factory_test.TestVirtualMachineDeployment04.approved.json b/builder/azure/arm/template_factory_test.TestVirtualMachineDeployment04.approved.json deleted file mode 100644 index 4c69b352a..000000000 --- a/builder/azure/arm/template_factory_test.TestVirtualMachineDeployment04.approved.json +++ /dev/null @@ -1,180 +0,0 @@ -{ - "$schema": "http://schema.management.azure.com/schemas/2014-04-01-preview/deploymentTemplate.json", - "contentVersion": "1.0.0.0", - "parameters": { - "adminPassword": { - "type": "string" - }, - "adminUsername": { - "type": "string" - }, - "dataDiskName": { - "type": "string" - }, - "dnsNameForPublicIP": { - "type": "string" - }, - "nicName": { - "type": "string" - }, - "nsgName": { - "type": "string" - }, - "osDiskName": { - "type": "string" - }, - "publicIPAddressName": { - "type": "string" - }, - "storageAccountBlobEndpoint": { - "type": "string" - }, - "subnetName": { - "type": "string" - }, - "virtualNetworkName": { - "type": "string" - }, - "vmName": { - "type": "string" - }, - "vmSize": { - "type": "string" - } - }, - "resources": [ - { - "apiVersion": "[variables('publicIPAddressApiVersion')]", - "location": "[variables('location')]", - "name": "[parameters('publicIPAddressName')]", - "properties": { - "dnsSettings": { - "domainNameLabel": "[parameters('dnsNameForPublicIP')]" - }, - "publicIPAllocationMethod": "[variables('publicIPAddressType')]" - }, - "type": "Microsoft.Network/publicIPAddresses" - }, - { - "apiVersion": "[variables('virtualNetworksApiVersion')]", - "location": "[variables('location')]", - "name": "[variables('virtualNetworkName')]", - "properties": { - "addressSpace": { - "addressPrefixes": [ - "[variables('addressPrefix')]" - ] - }, - "subnets": [ - { - "name": "[variables('subnetName')]", - "properties": { - "addressPrefix": "[variables('subnetAddressPrefix')]" - } - } - ] - }, - "type": "Microsoft.Network/virtualNetworks" - }, - { - "apiVersion": "[variables('networkInterfacesApiVersion')]", - "dependsOn": [ - "[concat('Microsoft.Network/publicIPAddresses/', parameters('publicIPAddressName'))]", - "[concat('Microsoft.Network/virtualNetworks/', variables('virtualNetworkName'))]" - ], - "location": "[variables('location')]", - "name": "[parameters('nicName')]", - "properties": { - "ipConfigurations": [ - { - "name": "ipconfig", - "properties": { - "privateIPAllocationMethod": "Dynamic", - "publicIPAddress": { - "id": "[resourceId('Microsoft.Network/publicIPAddresses', parameters('publicIPAddressName'))]" - }, - "subnet": { - "id": "[variables('subnetRef')]" - } - } - } - ] - }, - "type": "Microsoft.Network/networkInterfaces" - }, - { - "apiVersion": "[variables('apiVersion')]", - "dependsOn": [ - "[concat('Microsoft.Network/networkInterfaces/', parameters('nicName'))]" - ], - "location": "[variables('location')]", - "name": "[parameters('vmName')]", - "properties": { - "diagnosticsProfile": { - "bootDiagnostics": { - "enabled": false - } - }, - "hardwareProfile": { - "vmSize": "[parameters('vmSize')]" - }, - "networkProfile": { - "networkInterfaces": [ - { - "id": "[resourceId('Microsoft.Network/networkInterfaces', parameters('nicName'))]" - } - ] - }, - "osProfile": { - "adminUsername": "[parameters('adminUsername')]", - "computerName": "[parameters('vmName')]", - "linuxConfiguration": { - "disablePasswordAuthentication": true, - "ssh": { - "publicKeys": [ - { - "keyData": "", - "path": "[variables('sshKeyPath')]" - } - ] - } - } - }, - "storageProfile": { - "osDisk": { - "caching": "ReadWrite", - "createOption": "FromImage", - "image": { - "uri": "https://localhost/custom.vhd" - }, - "name": "[parameters('osDiskName')]", - "osType": "Linux", - "vhd": { - "uri": "[concat(parameters('storageAccountBlobEndpoint'),variables('vmStorageAccountContainerName'),'/', parameters('osDiskName'),'.vhd')]" - } - } - } - }, - "type": "Microsoft.Compute/virtualMachines" - } - ], - "variables": { - "addressPrefix": "10.0.0.0/16", - "apiVersion": "2017-03-30", - "location": "[resourceGroup().location]", - "managedDiskApiVersion": "2017-03-30", - "networkInterfacesApiVersion": "2017-04-01", - "networkSecurityGroupsApiVersion": "2019-04-01", - "publicIPAddressApiVersion": "2017-04-01", - "publicIPAddressType": "Dynamic", - "sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]", - "subnetAddressPrefix": "10.0.0.0/24", - "subnetName": "[parameters('subnetName')]", - "subnetRef": "[concat(variables('vnetID'),'/subnets/',variables('subnetName'))]", - "virtualNetworkName": "[parameters('virtualNetworkName')]", - "virtualNetworkResourceGroup": "[resourceGroup().name]", - "virtualNetworksApiVersion": "2017-04-01", - "vmStorageAccountContainerName": "images", - "vnetID": "[resourceId(variables('virtualNetworkResourceGroup'), 'Microsoft.Network/virtualNetworks', variables('virtualNetworkName'))]" - } -} \ No newline at end of file diff --git a/builder/azure/arm/template_factory_test.TestVirtualMachineDeployment05.approved.json b/builder/azure/arm/template_factory_test.TestVirtualMachineDeployment05.approved.json deleted file mode 100644 index f4cc6f3ab..000000000 --- a/builder/azure/arm/template_factory_test.TestVirtualMachineDeployment05.approved.json +++ /dev/null @@ -1,141 +0,0 @@ -{ - "$schema": "http://schema.management.azure.com/schemas/2014-04-01-preview/deploymentTemplate.json", - "contentVersion": "1.0.0.0", - "parameters": { - "adminPassword": { - "type": "string" - }, - "adminUsername": { - "type": "string" - }, - "dataDiskName": { - "type": "string" - }, - "dnsNameForPublicIP": { - "type": "string" - }, - "nicName": { - "type": "string" - }, - "nsgName": { - "type": "string" - }, - "osDiskName": { - "type": "string" - }, - "publicIPAddressName": { - "type": "string" - }, - "storageAccountBlobEndpoint": { - "type": "string" - }, - "subnetName": { - "type": "string" - }, - "virtualNetworkName": { - "type": "string" - }, - "vmName": { - "type": "string" - }, - "vmSize": { - "type": "string" - } - }, - "resources": [ - { - "apiVersion": "[variables('networkInterfacesApiVersion')]", - "dependsOn": [], - "location": "[variables('location')]", - "name": "[parameters('nicName')]", - "properties": { - "ipConfigurations": [ - { - "name": "ipconfig", - "properties": { - "privateIPAllocationMethod": "Dynamic", - "subnet": { - "id": "[variables('subnetRef')]" - } - } - } - ] - }, - "type": "Microsoft.Network/networkInterfaces" - }, - { - "apiVersion": "[variables('apiVersion')]", - "dependsOn": [ - "[concat('Microsoft.Network/networkInterfaces/', parameters('nicName'))]" - ], - "location": "[variables('location')]", - "name": "[parameters('vmName')]", - "properties": { - "diagnosticsProfile": { - "bootDiagnostics": { - "enabled": false - } - }, - "hardwareProfile": { - "vmSize": "[parameters('vmSize')]" - }, - "networkProfile": { - "networkInterfaces": [ - { - "id": "[resourceId('Microsoft.Network/networkInterfaces', parameters('nicName'))]" - } - ] - }, - "osProfile": { - "adminPassword": "[parameters('adminPassword')]", - "adminUsername": "[parameters('adminUsername')]", - "computerName": "[parameters('vmName')]", - "linuxConfiguration": { - "ssh": { - "publicKeys": [ - { - "keyData": "", - "path": "[variables('sshKeyPath')]" - } - ] - } - } - }, - "storageProfile": { - "osDisk": { - "caching": "ReadWrite", - "createOption": "FromImage", - "image": { - "uri": "https://localhost/custom.vhd" - }, - "name": "[parameters('osDiskName')]", - "osType": "Linux", - "vhd": { - "uri": "[concat(parameters('storageAccountBlobEndpoint'),variables('vmStorageAccountContainerName'),'/', parameters('osDiskName'),'.vhd')]" - } - } - } - }, - "type": "Microsoft.Compute/virtualMachines" - } - ], - "variables": { - "addressPrefix": "10.0.0.0/16", - "apiVersion": "2017-03-30", - "location": "[resourceGroup().location]", - "managedDiskApiVersion": "2017-03-30", - "networkInterfacesApiVersion": "2017-04-01", - "networkSecurityGroupsApiVersion": "2019-04-01", - "publicIPAddressApiVersion": "2017-04-01", - "publicIPAddressType": "Dynamic", - "sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]", - "subnetAddressPrefix": "10.0.0.0/24", - "subnetName": "virtualNetworkSubnetName", - "subnetRef": "[concat(variables('vnetID'),'/subnets/',variables('subnetName'))]", - "virtualNetworkName": "virtualNetworkName", - "virtualNetworkResourceGroup": "virtualNetworkResourceGroupName", - "virtualNetworksApiVersion": "2017-04-01", - "vmStorageAccountContainerName": "images", - "vnetID": "[resourceId(variables('virtualNetworkResourceGroup'), 'Microsoft.Network/virtualNetworks', variables('virtualNetworkName'))]" - } -} \ No newline at end of file diff --git a/builder/azure/arm/template_factory_test.TestVirtualMachineDeployment06.approved.json b/builder/azure/arm/template_factory_test.TestVirtualMachineDeployment06.approved.json deleted file mode 100644 index 74774714a..000000000 --- a/builder/azure/arm/template_factory_test.TestVirtualMachineDeployment06.approved.json +++ /dev/null @@ -1,200 +0,0 @@ -{ - "$schema": "http://schema.management.azure.com/schemas/2014-04-01-preview/deploymentTemplate.json", - "contentVersion": "1.0.0.0", - "parameters": { - "adminPassword": { - "type": "string" - }, - "adminUsername": { - "type": "string" - }, - "dataDiskName": { - "type": "string" - }, - "dnsNameForPublicIP": { - "type": "string" - }, - "nicName": { - "type": "string" - }, - "nsgName": { - "type": "string" - }, - "osDiskName": { - "type": "string" - }, - "publicIPAddressName": { - "type": "string" - }, - "storageAccountBlobEndpoint": { - "type": "string" - }, - "subnetName": { - "type": "string" - }, - "virtualNetworkName": { - "type": "string" - }, - "vmName": { - "type": "string" - }, - "vmSize": { - "type": "string" - } - }, - "resources": [ - { - "apiVersion": "[variables('publicIPAddressApiVersion')]", - "location": "[variables('location')]", - "name": "[parameters('publicIPAddressName')]", - "properties": { - "dnsSettings": { - "domainNameLabel": "[parameters('dnsNameForPublicIP')]" - }, - "publicIPAllocationMethod": "[variables('publicIPAddressType')]" - }, - "tags": { - "tag01": "value01", - "tag02": "value02", - "tag03": "value03" - }, - "type": "Microsoft.Network/publicIPAddresses" - }, - { - "apiVersion": "[variables('virtualNetworksApiVersion')]", - "location": "[variables('location')]", - "name": "[variables('virtualNetworkName')]", - "properties": { - "addressSpace": { - "addressPrefixes": [ - "[variables('addressPrefix')]" - ] - }, - "subnets": [ - { - "name": "[variables('subnetName')]", - "properties": { - "addressPrefix": "[variables('subnetAddressPrefix')]" - } - } - ] - }, - "tags": { - "tag01": "value01", - "tag02": "value02", - "tag03": "value03" - }, - "type": "Microsoft.Network/virtualNetworks" - }, - { - "apiVersion": "[variables('networkInterfacesApiVersion')]", - "dependsOn": [ - "[concat('Microsoft.Network/publicIPAddresses/', parameters('publicIPAddressName'))]", - "[concat('Microsoft.Network/virtualNetworks/', variables('virtualNetworkName'))]" - ], - "location": "[variables('location')]", - "name": "[parameters('nicName')]", - "properties": { - "ipConfigurations": [ - { - "name": "ipconfig", - "properties": { - "privateIPAllocationMethod": "Dynamic", - "publicIPAddress": { - "id": "[resourceId('Microsoft.Network/publicIPAddresses', parameters('publicIPAddressName'))]" - }, - "subnet": { - "id": "[variables('subnetRef')]" - } - } - } - ] - }, - "tags": { - "tag01": "value01", - "tag02": "value02", - "tag03": "value03" - }, - "type": "Microsoft.Network/networkInterfaces" - }, - { - "apiVersion": "[variables('apiVersion')]", - "dependsOn": [ - "[concat('Microsoft.Network/networkInterfaces/', parameters('nicName'))]" - ], - "location": "[variables('location')]", - "name": "[parameters('vmName')]", - "properties": { - "diagnosticsProfile": { - "bootDiagnostics": { - "enabled": false - } - }, - "hardwareProfile": { - "vmSize": "[parameters('vmSize')]" - }, - "networkProfile": { - "networkInterfaces": [ - { - "id": "[resourceId('Microsoft.Network/networkInterfaces', parameters('nicName'))]" - } - ] - }, - "osProfile": { - "adminUsername": "[parameters('adminUsername')]", - "computerName": "[parameters('vmName')]", - "linuxConfiguration": { - "disablePasswordAuthentication": true, - "ssh": { - "publicKeys": [ - { - "keyData": "", - "path": "[variables('sshKeyPath')]" - } - ] - } - } - }, - "storageProfile": { - "osDisk": { - "caching": "ReadWrite", - "createOption": "FromImage", - "image": { - "uri": "https://localhost/custom.vhd" - }, - "name": "[parameters('osDiskName')]", - "osType": "Linux", - "vhd": { - "uri": "[concat(parameters('storageAccountBlobEndpoint'),variables('vmStorageAccountContainerName'),'/', parameters('osDiskName'),'.vhd')]" - } - } - } - }, - "tags": { - "tag01": "value01", - "tag02": "value02", - "tag03": "value03" - }, - "type": "Microsoft.Compute/virtualMachines" - } - ], - "variables": { - "addressPrefix": "10.0.0.0/16", - "apiVersion": "2017-03-30", - "location": "[resourceGroup().location]", - "managedDiskApiVersion": "2017-03-30", - "networkInterfacesApiVersion": "2017-04-01", - "networkSecurityGroupsApiVersion": "2019-04-01", - "publicIPAddressApiVersion": "2017-04-01", - "publicIPAddressType": "Dynamic", - "sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]", - "subnetAddressPrefix": "10.0.0.0/24", - "subnetName": "[parameters('subnetName')]", - "subnetRef": "[concat(variables('vnetID'),'/subnets/',variables('subnetName'))]", - "virtualNetworkName": "[parameters('virtualNetworkName')]", - "virtualNetworkResourceGroup": "[resourceGroup().name]", - "virtualNetworksApiVersion": "2017-04-01", - "vmStorageAccountContainerName": "images", - "vnetID": "[resourceId(variables('virtualNetworkResourceGroup'), 'Microsoft.Network/virtualNetworks', variables('virtualNetworkName'))]" - } -} \ No newline at end of file diff --git a/builder/azure/arm/template_factory_test.TestVirtualMachineDeployment07.approved.json b/builder/azure/arm/template_factory_test.TestVirtualMachineDeployment07.approved.json deleted file mode 100644 index 191719eec..000000000 --- a/builder/azure/arm/template_factory_test.TestVirtualMachineDeployment07.approved.json +++ /dev/null @@ -1,181 +0,0 @@ -{ - "$schema": "http://schema.management.azure.com/schemas/2014-04-01-preview/deploymentTemplate.json", - "contentVersion": "1.0.0.0", - "parameters": { - "adminPassword": { - "type": "string" - }, - "adminUsername": { - "type": "string" - }, - "dataDiskName": { - "type": "string" - }, - "dnsNameForPublicIP": { - "type": "string" - }, - "nicName": { - "type": "string" - }, - "nsgName": { - "type": "string" - }, - "osDiskName": { - "type": "string" - }, - "publicIPAddressName": { - "type": "string" - }, - "storageAccountBlobEndpoint": { - "type": "string" - }, - "subnetName": { - "type": "string" - }, - "virtualNetworkName": { - "type": "string" - }, - "vmName": { - "type": "string" - }, - "vmSize": { - "type": "string" - } - }, - "resources": [ - { - "apiVersion": "[variables('publicIPAddressApiVersion')]", - "location": "[variables('location')]", - "name": "[parameters('publicIPAddressName')]", - "properties": { - "dnsSettings": { - "domainNameLabel": "[parameters('dnsNameForPublicIP')]" - }, - "publicIPAllocationMethod": "[variables('publicIPAddressType')]" - }, - "type": "Microsoft.Network/publicIPAddresses" - }, - { - "apiVersion": "[variables('virtualNetworksApiVersion')]", - "location": "[variables('location')]", - "name": "[variables('virtualNetworkName')]", - "properties": { - "addressSpace": { - "addressPrefixes": [ - "[variables('addressPrefix')]" - ] - }, - "subnets": [ - { - "name": "[variables('subnetName')]", - "properties": { - "addressPrefix": "[variables('subnetAddressPrefix')]" - } - } - ] - }, - "type": "Microsoft.Network/virtualNetworks" - }, - { - "apiVersion": "[variables('networkInterfacesApiVersion')]", - "dependsOn": [ - "[concat('Microsoft.Network/publicIPAddresses/', parameters('publicIPAddressName'))]", - "[concat('Microsoft.Network/virtualNetworks/', variables('virtualNetworkName'))]" - ], - "location": "[variables('location')]", - "name": "[parameters('nicName')]", - "properties": { - "ipConfigurations": [ - { - "name": "ipconfig", - "properties": { - "privateIPAllocationMethod": "Dynamic", - "publicIPAddress": { - "id": "[resourceId('Microsoft.Network/publicIPAddresses', parameters('publicIPAddressName'))]" - }, - "subnet": { - "id": "[variables('subnetRef')]" - } - } - } - ] - }, - "type": "Microsoft.Network/networkInterfaces" - }, - { - "apiVersion": "[variables('apiVersion')]", - "dependsOn": [ - "[concat('Microsoft.Network/networkInterfaces/', parameters('nicName'))]" - ], - "location": "[variables('location')]", - "name": "[parameters('vmName')]", - "properties": { - "diagnosticsProfile": { - "bootDiagnostics": { - "enabled": false - } - }, - "hardwareProfile": { - "vmSize": "[parameters('vmSize')]" - }, - "networkProfile": { - "networkInterfaces": [ - { - "id": "[resourceId('Microsoft.Network/networkInterfaces', parameters('nicName'))]" - } - ] - }, - "osProfile": { - "adminPassword": "[parameters('adminPassword')]", - "adminUsername": "[parameters('adminUsername')]", - "computerName": "[parameters('vmName')]", - "customData": "I2Nsb3VkLWNvbmZpZwpncm93cGFydDoKICBtb2RlOiBvZmYK", - "linuxConfiguration": { - "ssh": { - "publicKeys": [ - { - "keyData": "", - "path": "[variables('sshKeyPath')]" - } - ] - } - } - }, - "storageProfile": { - "osDisk": { - "caching": "ReadWrite", - "createOption": "FromImage", - "image": { - "uri": "https://localhost/custom.vhd" - }, - "name": "[parameters('osDiskName')]", - "osType": "Linux", - "vhd": { - "uri": "[concat(parameters('storageAccountBlobEndpoint'),variables('vmStorageAccountContainerName'),'/', parameters('osDiskName'),'.vhd')]" - } - } - } - }, - "type": "Microsoft.Compute/virtualMachines" - } - ], - "variables": { - "addressPrefix": "10.0.0.0/16", - "apiVersion": "2017-03-30", - "location": "[resourceGroup().location]", - "managedDiskApiVersion": "2017-03-30", - "networkInterfacesApiVersion": "2017-04-01", - "networkSecurityGroupsApiVersion": "2019-04-01", - "publicIPAddressApiVersion": "2017-04-01", - "publicIPAddressType": "Dynamic", - "sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]", - "subnetAddressPrefix": "10.0.0.0/24", - "subnetName": "[parameters('subnetName')]", - "subnetRef": "[concat(variables('vnetID'),'/subnets/',variables('subnetName'))]", - "virtualNetworkName": "[parameters('virtualNetworkName')]", - "virtualNetworkResourceGroup": "[resourceGroup().name]", - "virtualNetworksApiVersion": "2017-04-01", - "vmStorageAccountContainerName": "images", - "vnetID": "[resourceId(variables('virtualNetworkResourceGroup'), 'Microsoft.Network/virtualNetworks', variables('virtualNetworkName'))]" - } -} \ No newline at end of file diff --git a/builder/azure/arm/template_factory_test.TestVirtualMachineDeployment08.approved.json b/builder/azure/arm/template_factory_test.TestVirtualMachineDeployment08.approved.json deleted file mode 100644 index bd1445920..000000000 --- a/builder/azure/arm/template_factory_test.TestVirtualMachineDeployment08.approved.json +++ /dev/null @@ -1,180 +0,0 @@ -{ - "$schema": "http://schema.management.azure.com/schemas/2014-04-01-preview/deploymentTemplate.json", - "contentVersion": "1.0.0.0", - "parameters": { - "adminPassword": { - "type": "string" - }, - "adminUsername": { - "type": "string" - }, - "dataDiskName": { - "type": "string" - }, - "dnsNameForPublicIP": { - "type": "string" - }, - "nicName": { - "type": "string" - }, - "nsgName": { - "type": "string" - }, - "osDiskName": { - "type": "string" - }, - "publicIPAddressName": { - "type": "string" - }, - "storageAccountBlobEndpoint": { - "type": "string" - }, - "subnetName": { - "type": "string" - }, - "virtualNetworkName": { - "type": "string" - }, - "vmName": { - "type": "string" - }, - "vmSize": { - "type": "string" - } - }, - "resources": [ - { - "apiVersion": "[variables('publicIPAddressApiVersion')]", - "location": "[variables('location')]", - "name": "[parameters('publicIPAddressName')]", - "properties": { - "dnsSettings": { - "domainNameLabel": "[parameters('dnsNameForPublicIP')]" - }, - "publicIPAllocationMethod": "[variables('publicIPAddressType')]" - }, - "type": "Microsoft.Network/publicIPAddresses" - }, - { - "apiVersion": "[variables('virtualNetworksApiVersion')]", - "location": "[variables('location')]", - "name": "[variables('virtualNetworkName')]", - "properties": { - "addressSpace": { - "addressPrefixes": [ - "[variables('addressPrefix')]" - ] - }, - "subnets": [ - { - "name": "[variables('subnetName')]", - "properties": { - "addressPrefix": "[variables('subnetAddressPrefix')]" - } - } - ] - }, - "type": "Microsoft.Network/virtualNetworks" - }, - { - "apiVersion": "[variables('networkInterfacesApiVersion')]", - "dependsOn": [ - "[concat('Microsoft.Network/publicIPAddresses/', parameters('publicIPAddressName'))]", - "[concat('Microsoft.Network/virtualNetworks/', variables('virtualNetworkName'))]" - ], - "location": "[variables('location')]", - "name": "[parameters('nicName')]", - "properties": { - "ipConfigurations": [ - { - "name": "ipconfig", - "properties": { - "privateIPAllocationMethod": "Dynamic", - "publicIPAddress": { - "id": "[resourceId('Microsoft.Network/publicIPAddresses', parameters('publicIPAddressName'))]" - }, - "subnet": { - "id": "[variables('subnetRef')]" - } - } - } - ] - }, - "type": "Microsoft.Network/networkInterfaces" - }, - { - "apiVersion": "[variables('apiVersion')]", - "dependsOn": [ - "[concat('Microsoft.Network/networkInterfaces/', parameters('nicName'))]" - ], - "location": "[variables('location')]", - "name": "[parameters('vmName')]", - "properties": { - "diagnosticsProfile": { - "bootDiagnostics": { - "enabled": false - } - }, - "hardwareProfile": { - "vmSize": "[parameters('vmSize')]" - }, - "networkProfile": { - "networkInterfaces": [ - { - "id": "[resourceId('Microsoft.Network/networkInterfaces', parameters('nicName'))]" - } - ] - }, - "osProfile": { - "adminUsername": "[parameters('adminUsername')]", - "computerName": "[parameters('vmName')]", - "linuxConfiguration": { - "disablePasswordAuthentication": true, - "ssh": { - "publicKeys": [ - { - "keyData": "", - "path": "[variables('sshKeyPath')]" - } - ] - } - } - }, - "storageProfile": { - "imageReference": { - "id": "" - }, - "osDisk": { - "caching": "ReadWrite", - "createOption": "FromImage", - "managedDisk": { - "storageAccountType": "Standard_LRS" - }, - "name": "[parameters('osDiskName')]", - "osType": "Linux" - } - } - }, - "type": "Microsoft.Compute/virtualMachines" - } - ], - "variables": { - "addressPrefix": "10.0.0.0/16", - "apiVersion": "2017-03-30", - "location": "[resourceGroup().location]", - "managedDiskApiVersion": "2017-03-30", - "networkInterfacesApiVersion": "2017-04-01", - "networkSecurityGroupsApiVersion": "2019-04-01", - "publicIPAddressApiVersion": "2017-04-01", - "publicIPAddressType": "Dynamic", - "sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]", - "subnetAddressPrefix": "10.0.0.0/24", - "subnetName": "[parameters('subnetName')]", - "subnetRef": "[concat(variables('vnetID'),'/subnets/',variables('subnetName'))]", - "virtualNetworkName": "[parameters('virtualNetworkName')]", - "virtualNetworkResourceGroup": "[resourceGroup().name]", - "virtualNetworksApiVersion": "2017-04-01", - "vmStorageAccountContainerName": "images", - "vnetID": "[resourceId(variables('virtualNetworkResourceGroup'), 'Microsoft.Network/virtualNetworks', variables('virtualNetworkName'))]" - } -} \ No newline at end of file diff --git a/builder/azure/arm/template_factory_test.TestVirtualMachineDeployment09.approved.json b/builder/azure/arm/template_factory_test.TestVirtualMachineDeployment09.approved.json deleted file mode 100644 index b9a8c8d79..000000000 --- a/builder/azure/arm/template_factory_test.TestVirtualMachineDeployment09.approved.json +++ /dev/null @@ -1,183 +0,0 @@ -{ - "$schema": "http://schema.management.azure.com/schemas/2014-04-01-preview/deploymentTemplate.json", - "contentVersion": "1.0.0.0", - "parameters": { - "adminPassword": { - "type": "string" - }, - "adminUsername": { - "type": "string" - }, - "dataDiskName": { - "type": "string" - }, - "dnsNameForPublicIP": { - "type": "string" - }, - "nicName": { - "type": "string" - }, - "nsgName": { - "type": "string" - }, - "osDiskName": { - "type": "string" - }, - "publicIPAddressName": { - "type": "string" - }, - "storageAccountBlobEndpoint": { - "type": "string" - }, - "subnetName": { - "type": "string" - }, - "virtualNetworkName": { - "type": "string" - }, - "vmName": { - "type": "string" - }, - "vmSize": { - "type": "string" - } - }, - "resources": [ - { - "apiVersion": "[variables('publicIPAddressApiVersion')]", - "location": "[variables('location')]", - "name": "[parameters('publicIPAddressName')]", - "properties": { - "dnsSettings": { - "domainNameLabel": "[parameters('dnsNameForPublicIP')]" - }, - "publicIPAllocationMethod": "[variables('publicIPAddressType')]" - }, - "type": "Microsoft.Network/publicIPAddresses" - }, - { - "apiVersion": "[variables('virtualNetworksApiVersion')]", - "location": "[variables('location')]", - "name": "[variables('virtualNetworkName')]", - "properties": { - "addressSpace": { - "addressPrefixes": [ - "[variables('addressPrefix')]" - ] - }, - "subnets": [ - { - "name": "[variables('subnetName')]", - "properties": { - "addressPrefix": "[variables('subnetAddressPrefix')]" - } - } - ] - }, - "type": "Microsoft.Network/virtualNetworks" - }, - { - "apiVersion": "[variables('networkInterfacesApiVersion')]", - "dependsOn": [ - "[concat('Microsoft.Network/publicIPAddresses/', parameters('publicIPAddressName'))]", - "[concat('Microsoft.Network/virtualNetworks/', variables('virtualNetworkName'))]" - ], - "location": "[variables('location')]", - "name": "[parameters('nicName')]", - "properties": { - "ipConfigurations": [ - { - "name": "ipconfig", - "properties": { - "privateIPAllocationMethod": "Dynamic", - "publicIPAddress": { - "id": "[resourceId('Microsoft.Network/publicIPAddresses', parameters('publicIPAddressName'))]" - }, - "subnet": { - "id": "[variables('subnetRef')]" - } - } - } - ] - }, - "type": "Microsoft.Network/networkInterfaces" - }, - { - "apiVersion": "[variables('apiVersion')]", - "dependsOn": [ - "[concat('Microsoft.Network/networkInterfaces/', parameters('nicName'))]" - ], - "location": "[variables('location')]", - "name": "[parameters('vmName')]", - "properties": { - "diagnosticsProfile": { - "bootDiagnostics": { - "enabled": false - } - }, - "hardwareProfile": { - "vmSize": "[parameters('vmSize')]" - }, - "networkProfile": { - "networkInterfaces": [ - { - "id": "[resourceId('Microsoft.Network/networkInterfaces', parameters('nicName'))]" - } - ] - }, - "osProfile": { - "adminPassword": "[parameters('adminPassword')]", - "adminUsername": "[parameters('adminUsername')]", - "computerName": "[parameters('vmName')]", - "linuxConfiguration": { - "ssh": { - "publicKeys": [ - { - "keyData": "", - "path": "[variables('sshKeyPath')]" - } - ] - } - } - }, - "storageProfile": { - "imageReference": { - "offer": "--image-offer--", - "publisher": "--image-publisher--", - "sku": "--image-sku--", - "version": "--version--" - }, - "osDisk": { - "caching": "ReadWrite", - "createOption": "FromImage", - "managedDisk": { - "storageAccountType": "Standard_LRS" - }, - "name": "[parameters('osDiskName')]", - "osType": "Linux" - } - } - }, - "type": "Microsoft.Compute/virtualMachines" - } - ], - "variables": { - "addressPrefix": "10.0.0.0/16", - "apiVersion": "2017-03-30", - "location": "[resourceGroup().location]", - "managedDiskApiVersion": "2017-03-30", - "networkInterfacesApiVersion": "2017-04-01", - "networkSecurityGroupsApiVersion": "2019-04-01", - "publicIPAddressApiVersion": "2017-04-01", - "publicIPAddressType": "Dynamic", - "sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]", - "subnetAddressPrefix": "10.0.0.0/24", - "subnetName": "[parameters('subnetName')]", - "subnetRef": "[concat(variables('vnetID'),'/subnets/',variables('subnetName'))]", - "virtualNetworkName": "[parameters('virtualNetworkName')]", - "virtualNetworkResourceGroup": "[resourceGroup().name]", - "virtualNetworksApiVersion": "2017-04-01", - "vmStorageAccountContainerName": "images", - "vnetID": "[resourceId(variables('virtualNetworkResourceGroup'), 'Microsoft.Network/virtualNetworks', variables('virtualNetworkName'))]" - } -} \ No newline at end of file diff --git a/builder/azure/arm/template_factory_test.TestVirtualMachineDeployment10.approved.json b/builder/azure/arm/template_factory_test.TestVirtualMachineDeployment10.approved.json deleted file mode 100644 index d87964955..000000000 --- a/builder/azure/arm/template_factory_test.TestVirtualMachineDeployment10.approved.json +++ /dev/null @@ -1,161 +0,0 @@ -{ - "$schema": "http://schema.management.azure.com/schemas/2014-04-01-preview/deploymentTemplate.json", - "contentVersion": "1.0.0.0", - "parameters": { - "adminPassword": { - "type": "string" - }, - "adminUsername": { - "type": "string" - }, - "dataDiskName": { - "type": "string" - }, - "dnsNameForPublicIP": { - "type": "string" - }, - "nicName": { - "type": "string" - }, - "nsgName": { - "type": "string" - }, - "osDiskName": { - "type": "string" - }, - "publicIPAddressName": { - "type": "string" - }, - "storageAccountBlobEndpoint": { - "type": "string" - }, - "subnetName": { - "type": "string" - }, - "virtualNetworkName": { - "type": "string" - }, - "vmName": { - "type": "string" - }, - "vmSize": { - "type": "string" - } - }, - "resources": [ - { - "apiVersion": "[variables('publicIPAddressApiVersion')]", - "location": "[variables('location')]", - "name": "[parameters('publicIPAddressName')]", - "properties": { - "dnsSettings": { - "domainNameLabel": "[parameters('dnsNameForPublicIP')]" - }, - "publicIPAllocationMethod": "[variables('publicIPAddressType')]" - }, - "type": "Microsoft.Network/publicIPAddresses" - }, - { - "apiVersion": "[variables('networkInterfacesApiVersion')]", - "dependsOn": [ - "[concat('Microsoft.Network/publicIPAddresses/', parameters('publicIPAddressName'))]" - ], - "location": "[variables('location')]", - "name": "[parameters('nicName')]", - "properties": { - "ipConfigurations": [ - { - "name": "ipconfig", - "properties": { - "privateIPAllocationMethod": "Dynamic", - "publicIPAddress": { - "id": "[resourceId('Microsoft.Network/publicIPAddresses', parameters('publicIPAddressName'))]" - }, - "subnet": { - "id": "[variables('subnetRef')]" - } - } - } - ] - }, - "type": "Microsoft.Network/networkInterfaces" - }, - { - "apiVersion": "[variables('apiVersion')]", - "dependsOn": [ - "[concat('Microsoft.Network/networkInterfaces/', parameters('nicName'))]" - ], - "location": "[variables('location')]", - "name": "[parameters('vmName')]", - "properties": { - "diagnosticsProfile": { - "bootDiagnostics": { - "enabled": false - } - }, - "hardwareProfile": { - "vmSize": "[parameters('vmSize')]" - }, - "networkProfile": { - "networkInterfaces": [ - { - "id": "[resourceId('Microsoft.Network/networkInterfaces', parameters('nicName'))]" - } - ] - }, - "osProfile": { - "adminUsername": "[parameters('adminUsername')]", - "computerName": "[parameters('vmName')]", - "linuxConfiguration": { - "disablePasswordAuthentication": true, - "ssh": { - "publicKeys": [ - { - "keyData": "", - "path": "[variables('sshKeyPath')]" - } - ] - } - } - }, - "storageProfile": { - "imageReference": { - "offer": "--image-offer--", - "publisher": "--image-publisher--", - "sku": "--image-sku--", - "version": "--version--" - }, - "osDisk": { - "caching": "ReadWrite", - "createOption": "FromImage", - "managedDisk": { - "storageAccountType": "Standard_LRS" - }, - "name": "[parameters('osDiskName')]", - "osType": "Linux" - } - } - }, - "type": "Microsoft.Compute/virtualMachines" - } - ], - "variables": { - "addressPrefix": "10.0.0.0/16", - "apiVersion": "2017-03-30", - "location": "[resourceGroup().location]", - "managedDiskApiVersion": "2017-03-30", - "networkInterfacesApiVersion": "2017-04-01", - "networkSecurityGroupsApiVersion": "2019-04-01", - "publicIPAddressApiVersion": "2017-04-01", - "publicIPAddressType": "Dynamic", - "sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]", - "subnetAddressPrefix": "10.0.0.0/24", - "subnetName": "--virtual_network_subnet_name--", - "subnetRef": "[concat(variables('vnetID'),'/subnets/',variables('subnetName'))]", - "virtualNetworkName": "--virtual_network_name--", - "virtualNetworkResourceGroup": "--virtual_network_resource_group_name--", - "virtualNetworksApiVersion": "2017-04-01", - "vmStorageAccountContainerName": "images", - "vnetID": "[resourceId(variables('virtualNetworkResourceGroup'), 'Microsoft.Network/virtualNetworks', variables('virtualNetworkName'))]" - } -} \ No newline at end of file diff --git a/builder/azure/arm/template_factory_test.TestVirtualMachineDeployment11.approved.json b/builder/azure/arm/template_factory_test.TestVirtualMachineDeployment11.approved.json deleted file mode 100644 index 96af5d2e2..000000000 --- a/builder/azure/arm/template_factory_test.TestVirtualMachineDeployment11.approved.json +++ /dev/null @@ -1,194 +0,0 @@ -{ - "$schema": "http://schema.management.azure.com/schemas/2014-04-01-preview/deploymentTemplate.json", - "contentVersion": "1.0.0.0", - "parameters": { - "adminPassword": { - "type": "string" - }, - "adminUsername": { - "type": "string" - }, - "dataDiskName": { - "type": "string" - }, - "dnsNameForPublicIP": { - "type": "string" - }, - "nicName": { - "type": "string" - }, - "nsgName": { - "type": "string" - }, - "osDiskName": { - "type": "string" - }, - "publicIPAddressName": { - "type": "string" - }, - "storageAccountBlobEndpoint": { - "type": "string" - }, - "subnetName": { - "type": "string" - }, - "virtualNetworkName": { - "type": "string" - }, - "vmName": { - "type": "string" - }, - "vmSize": { - "type": "string" - } - }, - "resources": [ - { - "apiVersion": "[variables('publicIPAddressApiVersion')]", - "location": "[variables('location')]", - "name": "[parameters('publicIPAddressName')]", - "properties": { - "dnsSettings": { - "domainNameLabel": "[parameters('dnsNameForPublicIP')]" - }, - "publicIPAllocationMethod": "[variables('publicIPAddressType')]" - }, - "type": "Microsoft.Network/publicIPAddresses" - }, - { - "apiVersion": "[variables('virtualNetworksApiVersion')]", - "location": "[variables('location')]", - "name": "[variables('virtualNetworkName')]", - "properties": { - "addressSpace": { - "addressPrefixes": [ - "[variables('addressPrefix')]" - ] - }, - "subnets": [ - { - "name": "[variables('subnetName')]", - "properties": { - "addressPrefix": "[variables('subnetAddressPrefix')]" - } - } - ] - }, - "type": "Microsoft.Network/virtualNetworks" - }, - { - "apiVersion": "[variables('networkInterfacesApiVersion')]", - "dependsOn": [ - "[concat('Microsoft.Network/publicIPAddresses/', parameters('publicIPAddressName'))]", - "[concat('Microsoft.Network/virtualNetworks/', variables('virtualNetworkName'))]" - ], - "location": "[variables('location')]", - "name": "[parameters('nicName')]", - "properties": { - "ipConfigurations": [ - { - "name": "ipconfig", - "properties": { - "privateIPAllocationMethod": "Dynamic", - "publicIPAddress": { - "id": "[resourceId('Microsoft.Network/publicIPAddresses', parameters('publicIPAddressName'))]" - }, - "subnet": { - "id": "[variables('subnetRef')]" - } - } - } - ] - }, - "type": "Microsoft.Network/networkInterfaces" - }, - { - "apiVersion": "[variables('apiVersion')]", - "dependsOn": [ - "[concat('Microsoft.Network/networkInterfaces/', parameters('nicName'))]" - ], - "location": "[variables('location')]", - "name": "[parameters('vmName')]", - "properties": { - "diagnosticsProfile": { - "bootDiagnostics": { - "enabled": false - } - }, - "hardwareProfile": { - "vmSize": "[parameters('vmSize')]" - }, - "networkProfile": { - "networkInterfaces": [ - { - "id": "[resourceId('Microsoft.Network/networkInterfaces', parameters('nicName'))]" - } - ] - }, - "osProfile": { - "adminPassword": "[parameters('adminPassword')]", - "adminUsername": "[parameters('adminUsername')]", - "computerName": "[parameters('vmName')]", - "linuxConfiguration": { - "ssh": { - "publicKeys": [ - { - "keyData": "", - "path": "[variables('sshKeyPath')]" - } - ] - } - } - }, - "storageProfile": { - "dataDisks": [ - { - "caching": "ReadWrite", - "createOption": "Empty", - "diskSizeGB": 32, - "lun": 0, - "name": "[concat(parameters('dataDiskName'),'-1')]", - "vhd": { - "uri": "[concat(parameters('storageAccountBlobEndpoint'),variables('vmStorageAccountContainerName'),'/',parameters('dataDiskName'),'-1','.vhd')]" - } - } - ], - "imageReference": { - "offer": "--image-offer--", - "publisher": "--image-publisher--", - "sku": "--image-sku--", - "version": "--version--" - }, - "osDisk": { - "caching": "ReadWrite", - "createOption": "FromImage", - "name": "[parameters('osDiskName')]", - "vhd": { - "uri": "[concat(parameters('storageAccountBlobEndpoint'),variables('vmStorageAccountContainerName'),'/', parameters('osDiskName'),'.vhd')]" - } - } - } - }, - "type": "Microsoft.Compute/virtualMachines" - } - ], - "variables": { - "addressPrefix": "10.0.0.0/16", - "apiVersion": "2017-03-30", - "location": "[resourceGroup().location]", - "managedDiskApiVersion": "2017-03-30", - "networkInterfacesApiVersion": "2017-04-01", - "networkSecurityGroupsApiVersion": "2019-04-01", - "publicIPAddressApiVersion": "2017-04-01", - "publicIPAddressType": "Dynamic", - "sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]", - "subnetAddressPrefix": "10.0.0.0/24", - "subnetName": "[parameters('subnetName')]", - "subnetRef": "[concat(variables('vnetID'),'/subnets/',variables('subnetName'))]", - "virtualNetworkName": "[parameters('virtualNetworkName')]", - "virtualNetworkResourceGroup": "[resourceGroup().name]", - "virtualNetworksApiVersion": "2017-04-01", - "vmStorageAccountContainerName": "images", - "vnetID": "[resourceId(variables('virtualNetworkResourceGroup'), 'Microsoft.Network/virtualNetworks', variables('virtualNetworkName'))]" - } -} \ No newline at end of file diff --git a/builder/azure/arm/template_factory_test.TestVirtualMachineDeployment12.approved.json b/builder/azure/arm/template_factory_test.TestVirtualMachineDeployment12.approved.json deleted file mode 100644 index c6addd27a..000000000 --- a/builder/azure/arm/template_factory_test.TestVirtualMachineDeployment12.approved.json +++ /dev/null @@ -1,195 +0,0 @@ -{ - "$schema": "http://schema.management.azure.com/schemas/2014-04-01-preview/deploymentTemplate.json", - "contentVersion": "1.0.0.0", - "parameters": { - "adminPassword": { - "type": "string" - }, - "adminUsername": { - "type": "string" - }, - "dataDiskName": { - "type": "string" - }, - "dnsNameForPublicIP": { - "type": "string" - }, - "nicName": { - "type": "string" - }, - "nsgName": { - "type": "string" - }, - "osDiskName": { - "type": "string" - }, - "publicIPAddressName": { - "type": "string" - }, - "storageAccountBlobEndpoint": { - "type": "string" - }, - "subnetName": { - "type": "string" - }, - "virtualNetworkName": { - "type": "string" - }, - "vmName": { - "type": "string" - }, - "vmSize": { - "type": "string" - } - }, - "resources": [ - { - "apiVersion": "[variables('publicIPAddressApiVersion')]", - "location": "[variables('location')]", - "name": "[parameters('publicIPAddressName')]", - "properties": { - "dnsSettings": { - "domainNameLabel": "[parameters('dnsNameForPublicIP')]" - }, - "publicIPAllocationMethod": "[variables('publicIPAddressType')]" - }, - "type": "Microsoft.Network/publicIPAddresses" - }, - { - "apiVersion": "[variables('virtualNetworksApiVersion')]", - "location": "[variables('location')]", - "name": "[variables('virtualNetworkName')]", - "properties": { - "addressSpace": { - "addressPrefixes": [ - "[variables('addressPrefix')]" - ] - }, - "subnets": [ - { - "name": "[variables('subnetName')]", - "properties": { - "addressPrefix": "[variables('subnetAddressPrefix')]" - } - } - ] - }, - "type": "Microsoft.Network/virtualNetworks" - }, - { - "apiVersion": "[variables('networkInterfacesApiVersion')]", - "dependsOn": [ - "[concat('Microsoft.Network/publicIPAddresses/', parameters('publicIPAddressName'))]", - "[concat('Microsoft.Network/virtualNetworks/', variables('virtualNetworkName'))]" - ], - "location": "[variables('location')]", - "name": "[parameters('nicName')]", - "properties": { - "ipConfigurations": [ - { - "name": "ipconfig", - "properties": { - "privateIPAllocationMethod": "Dynamic", - "publicIPAddress": { - "id": "[resourceId('Microsoft.Network/publicIPAddresses', parameters('publicIPAddressName'))]" - }, - "subnet": { - "id": "[variables('subnetRef')]" - } - } - } - ] - }, - "type": "Microsoft.Network/networkInterfaces" - }, - { - "apiVersion": "[variables('apiVersion')]", - "dependsOn": [ - "[concat('Microsoft.Network/networkInterfaces/', parameters('nicName'))]" - ], - "location": "[variables('location')]", - "name": "[parameters('vmName')]", - "properties": { - "diagnosticsProfile": { - "bootDiagnostics": { - "enabled": false - } - }, - "hardwareProfile": { - "vmSize": "[parameters('vmSize')]" - }, - "networkProfile": { - "networkInterfaces": [ - { - "id": "[resourceId('Microsoft.Network/networkInterfaces', parameters('nicName'))]" - } - ] - }, - "osProfile": { - "adminUsername": "[parameters('adminUsername')]", - "computerName": "[parameters('vmName')]", - "linuxConfiguration": { - "disablePasswordAuthentication": true, - "ssh": { - "publicKeys": [ - { - "keyData": "", - "path": "[variables('sshKeyPath')]" - } - ] - } - } - }, - "storageProfile": { - "dataDisks": [ - { - "caching": "ReadWrite", - "createOption": "Empty", - "diskSizeGB": 32, - "lun": 0, - "managedDisk": { - "storageAccountType": "Standard_LRS" - }, - "name": "[concat(parameters('dataDiskName'),'-1')]" - } - ], - "imageReference": { - "offer": "--image-offer--", - "publisher": "--image-publisher--", - "sku": "--image-sku--", - "version": "--version--" - }, - "osDisk": { - "caching": "ReadWrite", - "createOption": "FromImage", - "managedDisk": { - "storageAccountType": "Standard_LRS" - }, - "name": "[parameters('osDiskName')]", - "osType": "Linux" - } - } - }, - "type": "Microsoft.Compute/virtualMachines" - } - ], - "variables": { - "addressPrefix": "10.0.0.0/16", - "apiVersion": "2017-03-30", - "location": "[resourceGroup().location]", - "managedDiskApiVersion": "2017-03-30", - "networkInterfacesApiVersion": "2017-04-01", - "networkSecurityGroupsApiVersion": "2019-04-01", - "publicIPAddressApiVersion": "2017-04-01", - "publicIPAddressType": "Dynamic", - "sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]", - "subnetAddressPrefix": "10.0.0.0/24", - "subnetName": "[parameters('subnetName')]", - "subnetRef": "[concat(variables('vnetID'),'/subnets/',variables('subnetName'))]", - "virtualNetworkName": "[parameters('virtualNetworkName')]", - "virtualNetworkResourceGroup": "[resourceGroup().name]", - "virtualNetworksApiVersion": "2017-04-01", - "vmStorageAccountContainerName": "images", - "vnetID": "[resourceId(variables('virtualNetworkResourceGroup'), 'Microsoft.Network/virtualNetworks', variables('virtualNetworkName'))]" - } -} \ No newline at end of file diff --git a/builder/azure/arm/template_factory_test.TestVirtualMachineDeployment13.approved.json b/builder/azure/arm/template_factory_test.TestVirtualMachineDeployment13.approved.json deleted file mode 100644 index 66d3689c5..000000000 --- a/builder/azure/arm/template_factory_test.TestVirtualMachineDeployment13.approved.json +++ /dev/null @@ -1,230 +0,0 @@ -{ - "$schema": "http://schema.management.azure.com/schemas/2014-04-01-preview/deploymentTemplate.json", - "contentVersion": "1.0.0.0", - "parameters": { - "adminPassword": { - "type": "string" - }, - "adminUsername": { - "type": "string" - }, - "dataDiskName": { - "type": "string" - }, - "dnsNameForPublicIP": { - "type": "string" - }, - "nicName": { - "type": "string" - }, - "nsgName": { - "type": "string" - }, - "osDiskName": { - "type": "string" - }, - "publicIPAddressName": { - "type": "string" - }, - "storageAccountBlobEndpoint": { - "type": "string" - }, - "subnetName": { - "type": "string" - }, - "virtualNetworkName": { - "type": "string" - }, - "vmName": { - "type": "string" - }, - "vmSize": { - "type": "string" - } - }, - "resources": [ - { - "apiVersion": "[variables('publicIPAddressApiVersion')]", - "location": "[variables('location')]", - "name": "[parameters('publicIPAddressName')]", - "properties": { - "dnsSettings": { - "domainNameLabel": "[parameters('dnsNameForPublicIP')]" - }, - "publicIPAllocationMethod": "[variables('publicIPAddressType')]" - }, - "type": "Microsoft.Network/publicIPAddresses" - }, - { - "apiVersion": "[variables('networkInterfacesApiVersion')]", - "dependsOn": [ - "[concat('Microsoft.Network/publicIPAddresses/', parameters('publicIPAddressName'))]", - "[concat('Microsoft.Network/virtualNetworks/', variables('virtualNetworkName'))]" - ], - "location": "[variables('location')]", - "name": "[parameters('nicName')]", - "properties": { - "ipConfigurations": [ - { - "name": "ipconfig", - "properties": { - "privateIPAllocationMethod": "Dynamic", - "publicIPAddress": { - "id": "[resourceId('Microsoft.Network/publicIPAddresses', parameters('publicIPAddressName'))]" - }, - "subnet": { - "id": "[variables('subnetRef')]" - } - } - } - ] - }, - "type": "Microsoft.Network/networkInterfaces" - }, - { - "apiVersion": "[variables('apiVersion')]", - "dependsOn": [ - "[concat('Microsoft.Network/networkInterfaces/', parameters('nicName'))]" - ], - "location": "[variables('location')]", - "name": "[parameters('vmName')]", - "properties": { - "diagnosticsProfile": { - "bootDiagnostics": { - "enabled": false - } - }, - "hardwareProfile": { - "vmSize": "[parameters('vmSize')]" - }, - "networkProfile": { - "networkInterfaces": [ - { - "id": "[resourceId('Microsoft.Network/networkInterfaces', parameters('nicName'))]" - } - ] - }, - "osProfile": { - "adminPassword": "[parameters('adminPassword')]", - "adminUsername": "[parameters('adminUsername')]", - "computerName": "[parameters('vmName')]", - "secrets": [ - { - "sourceVault": { - "id": "[resourceId(resourceGroup().name, 'Microsoft.KeyVault/vaults', '--keyvault-name--')]" - }, - "vaultCertificates": [ - { - "certificateStore": "My", - "certificateUrl": "" - } - ] - } - ], - "windowsConfiguration": { - "provisionVMAgent": true, - "winRM": { - "listeners": [ - { - "certificateUrl": "", - "protocol": "https" - } - ] - } - } - }, - "storageProfile": { - "imageReference": { - "offer": "--image-offer--", - "publisher": "--image-publisher--", - "sku": "--image-sku--", - "version": "--version--" - }, - "osDisk": { - "caching": "ReadWrite", - "createOption": "FromImage", - "managedDisk": { - "storageAccountType": "Standard_LRS" - }, - "name": "[parameters('osDiskName')]", - "osType": "Windows" - } - } - }, - "type": "Microsoft.Compute/virtualMachines" - }, - { - "apiVersion": "[variables('networkSecurityGroupsApiVersion')]", - "location": "[variables('location')]", - "name": "[parameters('nsgName')]", - "properties": { - "securityRules": [ - { - "name": "AllowIPsToSshWinRMInbound", - "properties": { - "access": "Allow", - "description": "Allow inbound traffic from specified IP addresses", - "destinationAddressPrefix": "VirtualNetwork", - "destinationPortRange": "5985", - "direction": "Inbound", - "priority": 100, - "protocol": "Tcp", - "sourceAddressPrefixes": [ - "127.0.0.1", - "192.168.100.0/24" - ], - "sourcePortRange": "*" - } - } - ] - }, - "type": "Microsoft.Network/networkSecurityGroups" - }, - { - "apiVersion": "[variables('virtualNetworksApiVersion')]", - "dependsOn": [ - "[concat('Microsoft.Network/networkSecurityGroups/', parameters('nsgName'))]" - ], - "location": "[variables('location')]", - "name": "[variables('virtualNetworkName')]", - "properties": { - "addressSpace": { - "addressPrefixes": [ - "[variables('addressPrefix')]" - ] - }, - "subnets": [ - { - "name": "[variables('subnetName')]", - "properties": { - "addressPrefix": "[variables('subnetAddressPrefix')]", - "networkSecurityGroup": { - "id": "[resourceId('Microsoft.Network/networkSecurityGroups', parameters('nsgName'))]" - } - } - } - ] - }, - "type": "Microsoft.Network/virtualNetworks" - } - ], - "variables": { - "addressPrefix": "10.0.0.0/16", - "apiVersion": "2017-03-30", - "location": "[resourceGroup().location]", - "managedDiskApiVersion": "2017-03-30", - "networkInterfacesApiVersion": "2017-04-01", - "networkSecurityGroupsApiVersion": "2019-04-01", - "publicIPAddressApiVersion": "2017-04-01", - "publicIPAddressType": "Dynamic", - "sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]", - "subnetAddressPrefix": "10.0.0.0/24", - "subnetName": "[parameters('subnetName')]", - "subnetRef": "[concat(variables('vnetID'),'/subnets/',variables('subnetName'))]", - "virtualNetworkName": "[parameters('virtualNetworkName')]", - "virtualNetworkResourceGroup": "[resourceGroup().name]", - "virtualNetworksApiVersion": "2017-04-01", - "vmStorageAccountContainerName": "images", - "vnetID": "[resourceId(variables('virtualNetworkResourceGroup'), 'Microsoft.Network/virtualNetworks', variables('virtualNetworkName'))]" - } -} \ No newline at end of file diff --git a/builder/azure/arm/template_factory_test.TestVirtualMachineDeployment14.approved.json b/builder/azure/arm/template_factory_test.TestVirtualMachineDeployment14.approved.json deleted file mode 100644 index 36182b82d..000000000 --- a/builder/azure/arm/template_factory_test.TestVirtualMachineDeployment14.approved.json +++ /dev/null @@ -1,181 +0,0 @@ -{ - "$schema": "http://schema.management.azure.com/schemas/2014-04-01-preview/deploymentTemplate.json", - "contentVersion": "1.0.0.0", - "parameters": { - "adminPassword": { - "type": "string" - }, - "adminUsername": { - "type": "string" - }, - "dataDiskName": { - "type": "string" - }, - "dnsNameForPublicIP": { - "type": "string" - }, - "nicName": { - "type": "string" - }, - "nsgName": { - "type": "string" - }, - "osDiskName": { - "type": "string" - }, - "publicIPAddressName": { - "type": "string" - }, - "storageAccountBlobEndpoint": { - "type": "string" - }, - "subnetName": { - "type": "string" - }, - "virtualNetworkName": { - "type": "string" - }, - "vmName": { - "type": "string" - }, - "vmSize": { - "type": "string" - } - }, - "resources": [ - { - "apiVersion": "[variables('publicIPAddressApiVersion')]", - "location": "[variables('location')]", - "name": "[parameters('publicIPAddressName')]", - "properties": { - "dnsSettings": { - "domainNameLabel": "[parameters('dnsNameForPublicIP')]" - }, - "publicIPAllocationMethod": "[variables('publicIPAddressType')]" - }, - "type": "Microsoft.Network/publicIPAddresses" - }, - { - "apiVersion": "[variables('virtualNetworksApiVersion')]", - "location": "[variables('location')]", - "name": "[variables('virtualNetworkName')]", - "properties": { - "addressSpace": { - "addressPrefixes": [ - "[variables('addressPrefix')]" - ] - }, - "subnets": [ - { - "name": "[variables('subnetName')]", - "properties": { - "addressPrefix": "[variables('subnetAddressPrefix')]" - } - } - ] - }, - "type": "Microsoft.Network/virtualNetworks" - }, - { - "apiVersion": "[variables('networkInterfacesApiVersion')]", - "dependsOn": [ - "[concat('Microsoft.Network/publicIPAddresses/', parameters('publicIPAddressName'))]", - "[concat('Microsoft.Network/virtualNetworks/', variables('virtualNetworkName'))]" - ], - "location": "[variables('location')]", - "name": "[parameters('nicName')]", - "properties": { - "ipConfigurations": [ - { - "name": "ipconfig", - "properties": { - "privateIPAllocationMethod": "Dynamic", - "publicIPAddress": { - "id": "[resourceId('Microsoft.Network/publicIPAddresses', parameters('publicIPAddressName'))]" - }, - "subnet": { - "id": "[variables('subnetRef')]" - } - } - } - ] - }, - "type": "Microsoft.Network/networkInterfaces" - }, - { - "apiVersion": "[variables('apiVersion')]", - "dependsOn": [ - "[concat('Microsoft.Network/networkInterfaces/', parameters('nicName'))]" - ], - "location": "[variables('location')]", - "name": "[parameters('vmName')]", - "properties": { - "diagnosticsProfile": { - "bootDiagnostics": { - "enabled": true, - "storageUri": "https://diagstgaccnt.blob.core.windows.net" - } - }, - "hardwareProfile": { - "vmSize": "[parameters('vmSize')]" - }, - "networkProfile": { - "networkInterfaces": [ - { - "id": "[resourceId('Microsoft.Network/networkInterfaces', parameters('nicName'))]" - } - ] - }, - "osProfile": { - "adminUsername": "[parameters('adminUsername')]", - "computerName": "[parameters('vmName')]", - "linuxConfiguration": { - "disablePasswordAuthentication": true, - "ssh": { - "publicKeys": [ - { - "keyData": "", - "path": "[variables('sshKeyPath')]" - } - ] - } - } - }, - "storageProfile": { - "imageReference": { - "id": "" - }, - "osDisk": { - "caching": "ReadWrite", - "createOption": "FromImage", - "managedDisk": { - "storageAccountType": "Standard_LRS" - }, - "name": "[parameters('osDiskName')]", - "osType": "Linux" - } - } - }, - "type": "Microsoft.Compute/virtualMachines" - } - ], - "variables": { - "addressPrefix": "10.0.0.0/16", - "apiVersion": "2017-03-30", - "location": "[resourceGroup().location]", - "managedDiskApiVersion": "2017-03-30", - "networkInterfacesApiVersion": "2017-04-01", - "networkSecurityGroupsApiVersion": "2019-04-01", - "publicIPAddressApiVersion": "2017-04-01", - "publicIPAddressType": "Dynamic", - "sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]", - "subnetAddressPrefix": "10.0.0.0/24", - "subnetName": "[parameters('subnetName')]", - "subnetRef": "[concat(variables('vnetID'),'/subnets/',variables('subnetName'))]", - "virtualNetworkName": "[parameters('virtualNetworkName')]", - "virtualNetworkResourceGroup": "[resourceGroup().name]", - "virtualNetworksApiVersion": "2017-04-01", - "vmStorageAccountContainerName": "images", - "vnetID": "[resourceId(variables('virtualNetworkResourceGroup'), 'Microsoft.Network/virtualNetworks', variables('virtualNetworkName'))]" - } -} \ No newline at end of file diff --git a/builder/azure/arm/template_factory_test.go b/builder/azure/arm/template_factory_test.go deleted file mode 100644 index f0e026a26..000000000 --- a/builder/azure/arm/template_factory_test.go +++ /dev/null @@ -1,659 +0,0 @@ -package arm - -import ( - "encoding/base64" - "encoding/json" - "testing" - - "github.com/Azure/azure-sdk-for-go/services/resources/mgmt/2018-02-01/resources" - approvaltests "github.com/approvals/go-approval-tests" - "github.com/hashicorp/packer/builder/azure/common/constants" - "github.com/hashicorp/packer/builder/azure/common/template" -) - -// Ensure the link values are not set, and the concrete values are set. -func TestVirtualMachineDeployment00(t *testing.T) { - var c Config - c.Prepare(getArmBuilderConfiguration(), getPackerConfiguration()) - deployment, err := GetVirtualMachineDeployment(&c) - if err != nil { - t.Fatal(err) - } - - if deployment.Properties.Mode != resources.Incremental { - t.Errorf("Expected deployment.Properties.Mode to be %s, but got %s", resources.Incremental, deployment.Properties.Mode) - } - - if deployment.Properties.ParametersLink != nil { - t.Error("Expected the ParametersLink to be nil!") - } - - if deployment.Properties.TemplateLink != nil { - t.Error("Expected the TemplateLink to be nil!") - } - - if deployment.Properties.Parameters == nil { - t.Error("Expected the Parameters to not be nil!") - } - - if deployment.Properties.Template == nil { - t.Error("Expected the Template to not be nil!") - } -} - -// Ensure the Virtual Machine template is a valid JSON document. -func TestVirtualMachineDeployment01(t *testing.T) { - var c Config - c.Prepare(getArmBuilderConfiguration(), getPackerConfiguration()) - deployment, err := GetVirtualMachineDeployment(&c) - if err != nil { - t.Fatal(err) - } - - _, err = json.Marshal(deployment.Properties.Template) - if err != nil { - t.Fatal(err) - } -} - -// Ensure the Virtual Machine template parameters are correct. -func TestVirtualMachineDeployment02(t *testing.T) { - var c Config - c.Prepare(getArmBuilderConfiguration(), getPackerConfiguration()) - deployment, err := GetVirtualMachineDeployment(&c) - if err != nil { - t.Fatal(err) - } - - bs, err := json.Marshal(deployment.Properties.Parameters) - if err != nil { - t.Fatal(err) - } - - var params template.TemplateParameters - err = json.Unmarshal(bs, ¶ms) - if err != nil { - t.Fatal(err) - } - - if params.AdminUsername.Value != c.UserName { - t.Errorf("Expected template parameter 'AdminUsername' to be %s, but got %s.", params.AdminUsername.Value, c.UserName) - } - if params.AdminPassword.Value != c.tmpAdminPassword { - t.Errorf("Expected template parameter 'AdminPassword' to be %s, but got %s.", params.AdminPassword.Value, c.tmpAdminPassword) - } - if params.DnsNameForPublicIP.Value != c.tmpComputeName { - t.Errorf("Expected template parameter 'DnsNameForPublicIP' to be %s, but got %s.", params.DnsNameForPublicIP.Value, c.tmpComputeName) - } - if params.OSDiskName.Value != c.tmpOSDiskName { - t.Errorf("Expected template parameter 'OSDiskName' to be %s, but got %s.", params.OSDiskName.Value, c.tmpOSDiskName) - } - if params.StorageAccountBlobEndpoint.Value != c.storageAccountBlobEndpoint { - t.Errorf("Expected template parameter 'StorageAccountBlobEndpoint' to be %s, but got %s.", params.StorageAccountBlobEndpoint.Value, c.storageAccountBlobEndpoint) - } - if params.VMSize.Value != c.VMSize { - t.Errorf("Expected template parameter 'VMSize' to be %s, but got %s.", params.VMSize.Value, c.VMSize) - } - if params.VMName.Value != c.tmpComputeName { - t.Errorf("Expected template parameter 'VMName' to be %s, but got %s.", params.VMName.Value, c.tmpComputeName) - } -} - -// Ensure the VM template is correct when using a market place image. -func TestVirtualMachineDeployment03(t *testing.T) { - m := getArmBuilderConfiguration() - m["image_publisher"] = "ImagePublisher" - m["image_offer"] = "ImageOffer" - m["image_sku"] = "ImageSku" - m["image_version"] = "ImageVersion" - - var c Config - _, err := c.Prepare(m, getPackerConfiguration(), getPackerSSHPasswordCommunicatorConfiguration()) - if err != nil { - t.Fatal(err) - } - deployment, err := GetVirtualMachineDeployment(&c) - if err != nil { - t.Fatal(err) - } - - err = approvaltests.VerifyJSONStruct(t, deployment.Properties.Template) - if err != nil { - t.Fatal(err) - } -} - -// Ensure the VM template is correct when using a custom image. -func TestVirtualMachineDeployment04(t *testing.T) { - config := map[string]string{ - "capture_name_prefix": "ignore", - "capture_container_name": "ignore", - "location": "ignore", - "image_url": "https://localhost/custom.vhd", - "resource_group_name": "ignore", - "storage_account": "ignore", - "subscription_id": "ignore", - "os_type": constants.Target_Linux, - "communicator": "none", - } - - var c Config - _, err := c.Prepare(config, getPackerConfiguration()) - if err != nil { - t.Fatal(err) - } - - deployment, err := GetVirtualMachineDeployment(&c) - if err != nil { - t.Fatal(err) - } - - err = approvaltests.VerifyJSONStruct(t, deployment.Properties.Template) - if err != nil { - t.Fatal(err) - } -} - -func TestVirtualMachineDeployment05(t *testing.T) { - config := map[string]string{ - "capture_name_prefix": "ignore", - "capture_container_name": "ignore", - "location": "ignore", - "image_url": "https://localhost/custom.vhd", - "resource_group_name": "ignore", - "storage_account": "ignore", - "subscription_id": "ignore", - "os_type": constants.Target_Linux, - "communicator": "none", - "virtual_network_name": "virtualNetworkName", - "virtual_network_resource_group_name": "virtualNetworkResourceGroupName", - "virtual_network_subnet_name": "virtualNetworkSubnetName", - } - - var c Config - _, err := c.Prepare(config, getPackerConfiguration(), getPackerSSHPasswordCommunicatorConfiguration()) - if err != nil { - t.Fatal(err) - } - - deployment, err := GetVirtualMachineDeployment(&c) - if err != nil { - t.Fatal(err) - } - - err = approvaltests.VerifyJSONStruct(t, deployment.Properties.Template) - if err != nil { - t.Fatal(err) - } -} - -// Verify that tags are properly applied to every resource -func TestVirtualMachineDeployment06(t *testing.T) { - config := map[string]interface{}{ - "capture_name_prefix": "ignore", - "capture_container_name": "ignore", - "location": "ignore", - "image_url": "https://localhost/custom.vhd", - "resource_group_name": "ignore", - "storage_account": "ignore", - "subscription_id": "ignore", - "os_type": constants.Target_Linux, - "communicator": "none", - "azure_tags": map[string]string{ - "tag01": "value01", - "tag02": "value02", - "tag03": "value03", - }, - } - - var c Config - _, err := c.Prepare(config, getPackerConfiguration()) - if err != nil { - t.Fatal(err) - } - - deployment, err := GetVirtualMachineDeployment(&c) - if err != nil { - t.Fatal(err) - } - - err = approvaltests.VerifyJSONStruct(t, deployment.Properties.Template) - if err != nil { - t.Fatal(err) - } -} - -// Verify that custom data are properly inserted -func TestVirtualMachineDeployment07(t *testing.T) { - config := map[string]interface{}{ - "capture_name_prefix": "ignore", - "capture_container_name": "ignore", - "location": "ignore", - "image_url": "https://localhost/custom.vhd", - "resource_group_name": "ignore", - "storage_account": "ignore", - "subscription_id": "ignore", - "os_type": constants.Target_Linux, - "communicator": "none", - } - - var c Config - _, err := c.Prepare(config, getPackerConfiguration(), getPackerSSHPasswordCommunicatorConfiguration()) - if err != nil { - t.Fatal(err) - } - - // The user specifies a configuration value for the setting custom_data_file. - // The config type will read that file, and base64 encode it. The encoded - // contents are then assigned to Config's customData property, which are directly - // injected into the template. - // - // I am not aware of an easy to mimic this situation in a test without having - // a file on disk, which I am loathe to do. The alternative is to inject base64 - // encoded data myself, which is what I am doing here. - customData := `#cloud-config -growpart: - mode: off -` - base64CustomData := base64.StdEncoding.EncodeToString([]byte(customData)) - c.customData = base64CustomData - - deployment, err := GetVirtualMachineDeployment(&c) - if err != nil { - t.Fatal(err) - } - - err = approvaltests.VerifyJSONStruct(t, deployment.Properties.Template) - if err != nil { - t.Fatal(err) - } -} - -// Ensure the VM template is correct when building from a custom managed image. -func TestVirtualMachineDeployment08(t *testing.T) { - config := map[string]interface{}{ - "location": "ignore", - "subscription_id": "ignore", - "os_type": constants.Target_Linux, - "communicator": "none", - "custom_managed_image_resource_group_name": "CustomManagedImageResourceGroupName", - "custom_managed_image_name": "CustomManagedImageName", - "managed_image_name": "ManagedImageName", - "managed_image_resource_group_name": "ManagedImageResourceGroupName", - } - - var c Config - _, err := c.Prepare(config, getPackerConfiguration()) - if err != nil { - t.Fatal(err) - } - - deployment, err := GetVirtualMachineDeployment(&c) - if err != nil { - t.Fatal(err) - } - - err = approvaltests.VerifyJSONStruct(t, deployment.Properties.Template) - if err != nil { - t.Fatal(err) - } -} - -// Ensure the VM template is correct when building from a platform managed image. -func TestVirtualMachineDeployment09(t *testing.T) { - config := map[string]interface{}{ - "location": "ignore", - "subscription_id": "ignore", - "os_type": constants.Target_Linux, - "communicator": "none", - "image_publisher": "--image-publisher--", - "image_offer": "--image-offer--", - "image_sku": "--image-sku--", - "image_version": "--version--", - "managed_image_name": "ManagedImageName", - "managed_image_resource_group_name": "ManagedImageResourceGroupName", - } - - var c Config - _, err := c.Prepare(config, getPackerConfiguration(), getPackerSSHPasswordCommunicatorConfiguration()) - if err != nil { - t.Fatal(err) - } - - deployment, err := GetVirtualMachineDeployment(&c) - if err != nil { - t.Fatal(err) - } - - err = approvaltests.VerifyJSONStruct(t, deployment.Properties.Template) - if err != nil { - t.Fatal(err) - } -} - -// Ensure the VM template is correct when building with PublicIp and connect to Private Network -func TestVirtualMachineDeployment10(t *testing.T) { - config := map[string]interface{}{ - "location": "ignore", - "subscription_id": "ignore", - "os_type": constants.Target_Linux, - "communicator": "none", - "image_publisher": "--image-publisher--", - "image_offer": "--image-offer--", - "image_sku": "--image-sku--", - "image_version": "--version--", - - "virtual_network_resource_group_name": "--virtual_network_resource_group_name--", - "virtual_network_name": "--virtual_network_name--", - "virtual_network_subnet_name": "--virtual_network_subnet_name--", - "private_virtual_network_with_public_ip": true, - - "managed_image_name": "ManagedImageName", - "managed_image_resource_group_name": "ManagedImageResourceGroupName", - } - - var c Config - _, err := c.Prepare(config, getPackerConfiguration()) - if err != nil { - t.Fatal(err) - } - - deployment, err := GetVirtualMachineDeployment(&c) - if err != nil { - t.Fatal(err) - } - - err = approvaltests.VerifyJSONStruct(t, deployment.Properties.Template) - if err != nil { - t.Fatal(err) - } -} - -// Ensure the VM template is correct when building with additional unmanaged disks -func TestVirtualMachineDeployment11(t *testing.T) { - config := map[string]interface{}{ - "location": "ignore", - "subscription_id": "ignore", - "os_type": constants.Target_Linux, - "communicator": "none", - "image_publisher": "--image-publisher--", - "image_offer": "--image-offer--", - "image_sku": "--image-sku--", - "image_version": "--version--", - - "disk_additional_size": []uint{32}, - - "resource_group_name": "packergroup", - "storage_account": "packerartifacts", - "capture_name_prefix": "packer", - "capture_container_name": "packerimages", - } - - var c Config - _, err := c.Prepare(config, getPackerConfiguration(), getPackerSSHPasswordCommunicatorConfiguration()) - if err != nil { - t.Fatal(err) - } - - deployment, err := GetVirtualMachineDeployment(&c) - if err != nil { - t.Fatal(err) - } - - err = approvaltests.VerifyJSONStruct(t, deployment.Properties.Template) - if err != nil { - t.Fatal(err) - } -} - -// Ensure the VM template is correct when building with additional managed disks -func TestVirtualMachineDeployment12(t *testing.T) { - config := map[string]interface{}{ - "location": "ignore", - "subscription_id": "ignore", - "os_type": constants.Target_Linux, - "communicator": "none", - "image_publisher": "--image-publisher--", - "image_offer": "--image-offer--", - "image_sku": "--image-sku--", - "image_version": "--version--", - - "disk_additional_size": []uint{32}, - - "managed_image_name": "ManagedImageName", - "managed_image_resource_group_name": "ManagedImageResourceGroupName", - } - - var c Config - _, err := c.Prepare(config, getPackerConfiguration()) - if err != nil { - t.Fatal(err) - } - - deployment, err := GetVirtualMachineDeployment(&c) - if err != nil { - t.Fatal(err) - } - - err = approvaltests.VerifyJSONStruct(t, deployment.Properties.Template) - if err != nil { - t.Fatal(err) - } -} - -// Ensure the VM template is correct when building with list of allowed IP addresses -func TestVirtualMachineDeployment13(t *testing.T) { - config := map[string]interface{}{ - "location": "ignore", - "subscription_id": "ignore", - "os_type": constants.Target_Windows, - "communicator": "winrm", - "winrm_username": "ignore", - "image_publisher": "--image-publisher--", - "image_offer": "--image-offer--", - "image_sku": "--image-sku--", - "image_version": "--version--", - "managed_image_name": "ManagedImageName", - "managed_image_resource_group_name": "ManagedImageResourceGroupName", - "allowed_inbound_ip_addresses": []string{"127.0.0.1", "192.168.100.0/24"}, - } - - var c Config - _, err := c.Prepare(config, getPackerConfiguration()) - if err != nil { - t.Fatal(err) - } - c.tmpKeyVaultName = "--keyvault-name--" - - deployment, err := GetVirtualMachineDeployment(&c) - if err != nil { - t.Fatal(err) - } - - err = approvaltests.VerifyJSONStruct(t, deployment.Properties.Template) - if err != nil { - t.Fatal(err) - } -} - -// Ensure the VM template is correct when building with bootdiagnostics -func TestVirtualMachineDeployment14(t *testing.T) { - config := map[string]interface{}{ - "location": "ignore", - "subscription_id": "ignore", - "os_type": constants.Target_Linux, - "communicator": "none", - "custom_managed_image_resource_group_name": "CustomManagedImageResourceGroupName", - "custom_managed_image_name": "CustomManagedImageName", - "managed_image_name": "ManagedImageName", - "managed_image_resource_group_name": "ManagedImageResourceGroupName", - "boot_diag_storage_account": "diagstgaccnt", - } - - var c Config - _, err := c.Prepare(config, getPackerConfiguration()) - if err != nil { - t.Fatal(err) - } - - deployment, err := GetVirtualMachineDeployment(&c) - if err != nil { - t.Fatal(err) - } - - err = approvaltests.VerifyJSONStruct(t, deployment.Properties.Template) - if err != nil { - t.Fatal(err) - } -} - -// Ensure the link values are not set, and the concrete values are set. -func TestKeyVaultDeployment00(t *testing.T) { - var c Config - c.Prepare(getArmBuilderConfiguration(), getPackerConfiguration()) - deployment, err := GetKeyVaultDeployment(&c) - if err != nil { - t.Fatal(err) - } - - if deployment.Properties.Mode != resources.Incremental { - t.Errorf("Expected deployment.Properties.Mode to be %s, but got %s", resources.Incremental, deployment.Properties.Mode) - } - - if deployment.Properties.ParametersLink != nil { - t.Error("Expected the ParametersLink to be nil!") - } - - if deployment.Properties.TemplateLink != nil { - t.Error("Expected the TemplateLink to be nil!") - } - - if deployment.Properties.Parameters == nil { - t.Error("Expected the Parameters to not be nil!") - } - - if deployment.Properties.Template == nil { - t.Error("Expected the Template to not be nil!") - } -} - -// Ensure the KeyVault template is a valid JSON document. -func TestKeyVaultDeployment01(t *testing.T) { - var c Config - c.Prepare(getArmBuilderConfiguration(), getPackerConfiguration()) - deployment, err := GetKeyVaultDeployment(&c) - if err != nil { - t.Fatal(err) - } - - _, err = json.Marshal(deployment.Properties.Template) - if err != nil { - t.Fatal(err) - } -} - -// Ensure the KeyVault template parameters are correct. -func TestKeyVaultDeployment02(t *testing.T) { - var c Config - c.Prepare(getArmBuilderConfigurationWithWindows(), getPackerConfiguration()) - - deployment, err := GetKeyVaultDeployment(&c) - if err != nil { - t.Fatal(err) - } - - bs, err := json.Marshal(deployment.Properties.Parameters) - if err != nil { - t.Fatal(err) - } - - var params template.TemplateParameters - err = json.Unmarshal(bs, ¶ms) - if err != nil { - t.Fatal(err) - } - - if params.ObjectId.Value != c.ClientConfig.ObjectID { - t.Errorf("Expected template parameter 'ObjectId' to be %s, but got %s.", params.ObjectId.Value, c.ClientConfig.ObjectID) - } - if params.TenantId.Value != c.ClientConfig.TenantID { - t.Errorf("Expected template parameter 'TenantId' to be %s, but got %s.", params.TenantId.Value, c.ClientConfig.TenantID) - } - if params.KeyVaultName.Value != c.tmpKeyVaultName { - t.Errorf("Expected template parameter 'KeyVaultName' to be %s, but got %s.", params.KeyVaultName.Value, c.tmpKeyVaultName) - } - if params.KeyVaultSecretValue.Value != c.winrmCertificate { - t.Errorf("Expected template parameter 'KeyVaultSecretValue' to be %s, but got %s.", params.KeyVaultSecretValue.Value, c.winrmCertificate) - } -} - -// Ensure the KeyVault template is correct when tags are supplied. -func TestKeyVaultDeployment03(t *testing.T) { - tags := map[string]interface{}{ - "azure_tags": map[string]string{ - "tag01": "value01", - "tag02": "value02", - "tag03": "value03", - }, - } - - var c Config - c.Prepare(tags, getArmBuilderConfigurationWithWindows(), getPackerConfiguration()) - deployment, err := GetKeyVaultDeployment(&c) - if err != nil { - t.Fatal(err) - } - - err = approvaltests.VerifyJSONStruct(t, deployment.Properties.Template) - if err != nil { - t.Fatal(err) - } -} - -func TestPlanInfo01(t *testing.T) { - planInfo := map[string]interface{}{ - "plan_info": map[string]string{ - "plan_name": "planName00", - "plan_product": "planProduct00", - "plan_publisher": "planPublisher00", - }, - } - - var c Config - c.Prepare(planInfo, getArmBuilderConfiguration(), getPackerConfiguration()) - deployment, err := GetVirtualMachineDeployment(&c) - if err != nil { - t.Fatal(err) - } - - err = approvaltests.VerifyJSONStruct(t, deployment.Properties.Template) - if err != nil { - t.Fatal(err) - } -} - -func TestPlanInfo02(t *testing.T) { - planInfo := map[string]interface{}{ - "azure_tags": map[string]string{ - "dept": "engineering", - }, - "plan_info": map[string]string{ - "plan_name": "planName00", - "plan_product": "planProduct00", - "plan_publisher": "planPublisher00", - "plan_promotion_code": "planPromotionCode00", - }, - } - - var c Config - c.Prepare(planInfo, getArmBuilderConfiguration(), getPackerConfiguration()) - deployment, err := GetVirtualMachineDeployment(&c) - if err != nil { - t.Fatal(err) - } - - err = approvaltests.VerifyJSONStruct(t, deployment.Properties.Template) - if err != nil { - t.Fatal(err) - } -} diff --git a/builder/azure/arm/tempname.go b/builder/azure/arm/tempname.go deleted file mode 100644 index f566e267c..000000000 --- a/builder/azure/arm/tempname.go +++ /dev/null @@ -1,80 +0,0 @@ -package arm - -import ( - "fmt" - "strings" - - "github.com/hashicorp/packer-plugin-sdk/random" -) - -type TempName struct { - AdminPassword string - CertificatePassword string - ComputeName string - DeploymentName string - KeyVaultName string - ResourceGroupName string - OSDiskName string - DataDiskName string - NicName string - SubnetName string - PublicIPAddressName string - VirtualNetworkName string - NsgName string -} - -func NewTempName(p string) *TempName { - tempName := &TempName{} - - suffix := random.AlphaNumLower(5) - if p == "" { - p = "pkr" - suffix = random.AlphaNumLower(10) - } - - tempName.ComputeName = fmt.Sprintf("%svm%s", p, suffix) - tempName.DeploymentName = fmt.Sprintf("%sdp%s", p, suffix) - tempName.KeyVaultName = fmt.Sprintf("%skv%s", p, suffix) - tempName.OSDiskName = fmt.Sprintf("%sos%s", p, suffix) - tempName.DataDiskName = fmt.Sprintf("%sdd%s", p, suffix) - tempName.NicName = fmt.Sprintf("%sni%s", p, suffix) - tempName.PublicIPAddressName = fmt.Sprintf("%sip%s", p, suffix) - tempName.SubnetName = fmt.Sprintf("%ssn%s", p, suffix) - tempName.VirtualNetworkName = fmt.Sprintf("%svn%s", p, suffix) - tempName.NsgName = fmt.Sprintf("%ssg%s", p, suffix) - tempName.ResourceGroupName = fmt.Sprintf("%s-Resource-Group-%s", p, suffix) - - tempName.AdminPassword = generatePassword() - tempName.CertificatePassword = random.AlphaNum(32) - - return tempName -} - -// generate a password that is acceptable to Azure -// Three of the four items must be met. -// 1. Contains an uppercase character -// 2. Contains a lowercase character -// 3. Contains a numeric digit -// 4. Contains a special character -func generatePassword() string { - var s string - for i := 0; i < 100; i++ { - s := random.AlphaNum(32) - if !strings.ContainsAny(s, random.PossibleNumbers) { - continue - } - - if !strings.ContainsAny(s, random.PossibleLowerCase) { - continue - } - - if !strings.ContainsAny(s, random.PossibleUpperCase) { - continue - } - - return s - } - - // if an acceptable password cannot be generated in 100 tries, give up - return s -} diff --git a/builder/azure/arm/tempname_test.go b/builder/azure/arm/tempname_test.go deleted file mode 100644 index e982a5ad2..000000000 --- a/builder/azure/arm/tempname_test.go +++ /dev/null @@ -1,143 +0,0 @@ -package arm - -import ( - "strings" - "testing" - - "github.com/hashicorp/packer-plugin-sdk/random" -) - -func TestTempNameShouldCreatePrefixedRandomNames(t *testing.T) { - tempName := NewTempName("") - - if strings.Index(tempName.ComputeName, "pkrvm") != 0 { - t.Errorf("Expected ComputeName to begin with 'pkrvm', but got '%s'!", tempName.ComputeName) - } - - if strings.Index(tempName.DeploymentName, "pkrdp") != 0 { - t.Errorf("Expected ComputeName to begin with 'pkrdp', but got '%s'!", tempName.ComputeName) - } - - if strings.Index(tempName.OSDiskName, "pkros") != 0 { - t.Errorf("Expected OSDiskName to begin with 'pkros', but got '%s'!", tempName.OSDiskName) - } - - if strings.Index(tempName.NicName, "pkrni") != 0 { - t.Errorf("Expected NicName to begin with 'pkrni', but got '%s'!", tempName.NicName) - } - - if strings.Index(tempName.PublicIPAddressName, "pkrip") != 0 { - t.Errorf("Expected PublicIPAddressName to begin with 'pkrip', but got '%s'!", tempName.PublicIPAddressName) - } - - if strings.Index(tempName.ResourceGroupName, "pkr-Resource-Group-") != 0 { - t.Errorf("Expected ResourceGroupName to begin with 'pkr-Resource-Group-', but got '%s'!", tempName.ResourceGroupName) - } - - if strings.Index(tempName.SubnetName, "pkrsn") != 0 { - t.Errorf("Expected SubnetName to begin with 'pkrip', but got '%s'!", tempName.SubnetName) - } - - if strings.Index(tempName.VirtualNetworkName, "pkrvn") != 0 { - t.Errorf("Expected VirtualNetworkName to begin with 'pkrvn', but got '%s'!", tempName.VirtualNetworkName) - } - - if strings.Index(tempName.NsgName, "pkrsg") != 0 { - t.Errorf("Expected NsgName to begin with 'pkrsg', but got '%s'!", tempName.NsgName) - } -} - -func TestTempAdminPassword(t *testing.T) { - tempName := NewTempName("") - - if !strings.ContainsAny(tempName.AdminPassword, random.PossibleNumbers) { - t.Errorf("Expected AdminPassword to contain at least one of '%s'!", random.PossibleNumbers) - } - if !strings.ContainsAny(tempName.AdminPassword, random.PossibleLowerCase) { - t.Errorf("Expected AdminPassword to contain at least one of '%s'!", random.PossibleLowerCase) - } - if !strings.ContainsAny(tempName.AdminPassword, random.PossibleUpperCase) { - t.Errorf("Expected AdminPassword to contain at least one of '%s'!", random.PossibleUpperCase) - } -} - -func TestTempNameShouldHaveSameSuffix(t *testing.T) { - tempName := NewTempName("") - suffix := tempName.ComputeName[5:] - - if strings.HasSuffix(tempName.ComputeName, suffix) != true { - t.Errorf("Expected ComputeName to end with '%s', but the value is '%s'!", suffix, tempName.ComputeName) - } - - if strings.HasSuffix(tempName.DeploymentName, suffix) != true { - t.Errorf("Expected DeploymentName to end with '%s', but the value is '%s'!", suffix, tempName.DeploymentName) - } - - if strings.HasSuffix(tempName.OSDiskName, suffix) != true { - t.Errorf("Expected OSDiskName to end with '%s', but the value is '%s'!", suffix, tempName.OSDiskName) - } - - if strings.HasSuffix(tempName.NicName, suffix) != true { - t.Errorf("Expected NicName to end with '%s', but the value is '%s'!", suffix, tempName.PublicIPAddressName) - } - - if strings.HasSuffix(tempName.PublicIPAddressName, suffix) != true { - t.Errorf("Expected PublicIPAddressName to end with '%s', but the value is '%s'!", suffix, tempName.PublicIPAddressName) - } - - if strings.HasSuffix(tempName.ResourceGroupName, suffix) != true { - t.Errorf("Expected ResourceGroupName to end with '%s', but the value is '%s'!", suffix, tempName.ResourceGroupName) - } - - if strings.HasSuffix(tempName.SubnetName, suffix) != true { - t.Errorf("Expected SubnetName to end with '%s', but the value is '%s'!", suffix, tempName.SubnetName) - } - - if strings.HasSuffix(tempName.VirtualNetworkName, suffix) != true { - t.Errorf("Expected VirtualNetworkName to end with '%s', but the value is '%s'!", suffix, tempName.VirtualNetworkName) - } - - if strings.HasSuffix(tempName.NsgName, suffix) != true { - t.Errorf("Expected NsgName to end with '%s', but the value is '%s'!", suffix, tempName.NsgName) - } -} - -func TestTempNameShouldCreateCustomPrefix(t *testing.T) { - tempName := NewTempName("CustPrefix") - - if strings.Index(tempName.ComputeName, "CustPrefixvm") != 0 { - t.Errorf("Expected ComputeName to begin with 'CustPrefixvm', but got '%s'!", tempName.ComputeName) - } - - if strings.Index(tempName.DeploymentName, "CustPrefixdp") != 0 { - t.Errorf("Expected ComputeName to begin with 'CustPrefixdp', but got '%s'!", tempName.ComputeName) - } - - if strings.Index(tempName.OSDiskName, "CustPrefixos") != 0 { - t.Errorf("Expected OSDiskName to begin with 'CustPrefixos', but got '%s'!", tempName.OSDiskName) - } - - if strings.Index(tempName.NicName, "CustPrefixni") != 0 { - t.Errorf("Expected NicName to begin with 'CustPrefixni', but got '%s'!", tempName.NicName) - } - - if strings.Index(tempName.PublicIPAddressName, "CustPrefixip") != 0 { - t.Errorf("Expected PublicIPAddressName to begin with 'CustPrefixip', but got '%s'!", tempName.PublicIPAddressName) - } - - if strings.Index(tempName.ResourceGroupName, "CustPrefix-Resource-Group-") != 0 { - t.Errorf("Expected ResourceGroupName to begin with 'packer-Resource-Group-', but got '%s'!", tempName.ResourceGroupName) - } - - if strings.Index(tempName.SubnetName, "CustPrefixsn") != 0 { - t.Errorf("Expected SubnetName to begin with 'pkrip', but got '%s'!", tempName.SubnetName) - } - - if strings.Index(tempName.VirtualNetworkName, "CustPrefixvn") != 0 { - t.Errorf("Expected VirtualNetworkName to begin with 'CustPrefixvn', but got '%s'!", tempName.VirtualNetworkName) - } - - if strings.Index(tempName.NsgName, "CustPrefixsg") != 0 { - t.Errorf("Expected NsgName to begin with 'CustPrefixsg', but got '%s'!", tempName.NsgName) - } -} diff --git a/builder/azure/chroot/builder.go b/builder/azure/chroot/builder.go deleted file mode 100644 index 9130cb461..000000000 --- a/builder/azure/chroot/builder.go +++ /dev/null @@ -1,622 +0,0 @@ -//go:generate packer-sdc struct-markdown -//go:generate packer-sdc mapstructure-to-hcl2 -type Config - -// Package chroot is able to create an Azure managed image without requiring the -// launch of a new virtual machine for every build. It does this by attaching and -// mounting the root disk and chrooting into that directory. -// It then creates a managed image from that attached disk. -package chroot - -import ( - "context" - "errors" - "fmt" - "log" - "runtime" - "strings" - - "github.com/hashicorp/hcl/v2/hcldec" - "github.com/hashicorp/packer-plugin-sdk/chroot" - "github.com/hashicorp/packer-plugin-sdk/common" - "github.com/hashicorp/packer-plugin-sdk/multistep" - "github.com/hashicorp/packer-plugin-sdk/multistep/commonsteps" - packersdk "github.com/hashicorp/packer-plugin-sdk/packer" - "github.com/hashicorp/packer-plugin-sdk/template/config" - "github.com/hashicorp/packer-plugin-sdk/template/interpolate" - azcommon "github.com/hashicorp/packer/builder/azure/common" - "github.com/hashicorp/packer/builder/azure/common/client" - - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-12-01/compute" - "github.com/Azure/go-autorest/autorest/azure" - "github.com/mitchellh/mapstructure" -) - -// BuilderID is the unique ID for this builder -const BuilderID = "azure.chroot" - -// Config is the configuration that is chained through the steps and settable -// from the template. -type Config struct { - common.PackerConfig `mapstructure:",squash"` - - ClientConfig client.Config `mapstructure:",squash"` - - // When set to `true`, starts with an empty, unpartitioned disk. Defaults to `false`. - FromScratch bool `mapstructure:"from_scratch"` - // One of the following can be used as a source for an image: - // - a shared image version resource ID - // - a managed disk resource ID - // - a publisher:offer:sku:version specifier for plaform image sources. - Source string `mapstructure:"source" required:"true"` - sourceType sourceType - - // How to run shell commands. This may be useful to set environment variables or perhaps run - // a command with sudo or so on. This is a configuration template where the `.Command` variable - // is replaced with the command to be run. Defaults to `{{.Command}}`. - CommandWrapper string `mapstructure:"command_wrapper"` - // A series of commands to execute after attaching the root volume and before mounting the chroot. - // This is not required unless using `from_scratch`. If so, this should include any partitioning - // and filesystem creation commands. The path to the device is provided by `{{.Device}}`. - PreMountCommands []string `mapstructure:"pre_mount_commands"` - // Options to supply the `mount` command when mounting devices. Each option will be prefixed with - // `-o` and supplied to the `mount` command ran by Packer. Because this command is ran in a shell, - // user discretion is advised. See this manual page for the `mount` command for valid file system specific options. - MountOptions []string `mapstructure:"mount_options"` - // The partition number containing the / partition. By default this is the first partition of the volume. - MountPartition string `mapstructure:"mount_partition"` - // The path where the volume will be mounted. This is where the chroot environment will be. This defaults - // to `/mnt/packer-amazon-chroot-volumes/{{.Device}}`. This is a configuration template where the `.Device` - // variable is replaced with the name of the device where the volume is attached. - MountPath string `mapstructure:"mount_path"` - // As `pre_mount_commands`, but the commands are executed after mounting the root device and before the - // extra mount and copy steps. The device and mount path are provided by `{{.Device}}` and `{{.MountPath}}`. - PostMountCommands []string `mapstructure:"post_mount_commands"` - // This is a list of devices to mount into the chroot environment. This configuration parameter requires - // some additional documentation which is in the "Chroot Mounts" section below. Please read that section - // for more information on how to use this. - ChrootMounts [][]string `mapstructure:"chroot_mounts"` - // Paths to files on the running Azure instance that will be copied into the chroot environment prior to - // provisioning. Defaults to `/etc/resolv.conf` so that DNS lookups work. Pass an empty list to skip copying - // `/etc/resolv.conf`. You may need to do this if you're building an image that uses systemd. - CopyFiles []string `mapstructure:"copy_files"` - - // Try to resize the OS disk to this size on the first copy. Disks can only be englarged. If not specified, - // the disk will keep its original size. Required when using `from_scratch` - OSDiskSizeGB int32 `mapstructure:"os_disk_size_gb"` - // The [storage SKU](https://docs.microsoft.com/en-us/rest/api/compute/disks/createorupdate#diskstorageaccounttypes) - // to use for the OS Disk. Defaults to `Standard_LRS`. - OSDiskStorageAccountType string `mapstructure:"os_disk_storage_account_type"` - // The [cache type](https://docs.microsoft.com/en-us/rest/api/compute/images/createorupdate#cachingtypes) - // specified in the resulting image and for attaching it to the Packer VM. Defaults to `ReadOnly` - OSDiskCacheType string `mapstructure:"os_disk_cache_type"` - - // The [storage SKU](https://docs.microsoft.com/en-us/rest/api/compute/disks/createorupdate#diskstorageaccounttypes) - // to use for datadisks. Defaults to `Standard_LRS`. - DataDiskStorageAccountType string `mapstructure:"data_disk_storage_account_type"` - // The [cache type](https://docs.microsoft.com/en-us/rest/api/compute/images/createorupdate#cachingtypes) - // specified in the resulting image and for attaching it to the Packer VM. Defaults to `ReadOnly` - DataDiskCacheType string `mapstructure:"data_disk_cache_type"` - - // The [Hyper-V generation type](https://docs.microsoft.com/en-us/rest/api/compute/images/createorupdate#hypervgenerationtypes) for Managed Image output. - // Defaults to `V1`. - ImageHyperVGeneration string `mapstructure:"image_hyperv_generation"` - - // The id of the temporary OS disk that will be created. Will be generated if not set. - TemporaryOSDiskID string `mapstructure:"temporary_os_disk_id"` - - // The id of the temporary OS disk snapshot that will be created. Will be generated if not set. - TemporaryOSDiskSnapshotID string `mapstructure:"temporary_os_disk_snapshot_id"` - - // The prefix for the resource ids of the temporary data disks that will be created. The disks will be suffixed with a number. Will be generated if not set. - TemporaryDataDiskIDPrefix string `mapstructure:"temporary_data_disk_id_prefix"` - - // The prefix for the resource ids of the temporary data disk snapshots that will be created. The snapshots will be suffixed with a number. Will be generated if not set. - TemporaryDataDiskSnapshotIDPrefix string `mapstructure:"temporary_data_disk_snapshot_id"` - - // If set to `true`, leaves the temporary disks and snapshots behind in the Packer VM resource group. Defaults to `false` - SkipCleanup bool `mapstructure:"skip_cleanup"` - - // The managed image to create using this build. - ImageResourceID string `mapstructure:"image_resource_id"` - - // The shared image to create using this build. - SharedImageGalleryDestination SharedImageGalleryDestination `mapstructure:"shared_image_destination"` - - ctx interpolate.Context -} - -type sourceType string - -const ( - sourcePlatformImage sourceType = "PlatformImage" - sourceDisk sourceType = "Disk" - sourceSharedImage sourceType = "SharedImage" -) - -// GetContext implements ContextProvider to allow steps to use the config context -// for template interpolation -func (c *Config) GetContext() interpolate.Context { - return c.ctx -} - -type Builder struct { - config Config - runner multistep.Runner -} - -// verify interface implementation -var _ packersdk.Builder = &Builder{} - -func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() } - -func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) { - b.config.ctx.Funcs = azcommon.TemplateFuncs - b.config.ctx.Funcs["vm"] = CreateVMMetadataTemplateFunc() - md := &mapstructure.Metadata{} - err := config.Decode(&b.config, &config.DecodeOpts{ - PluginType: BuilderID, - Interpolate: true, - InterpolateContext: &b.config.ctx, - InterpolateFilter: &interpolate.RenderFilter{ - Exclude: []string{ - // these fields are interpolated in the steps, - // when more information is available - "command_wrapper", - "post_mount_commands", - "pre_mount_commands", - "mount_path", - }, - }, - Metadata: md, - }, raws...) - if err != nil { - return nil, nil, err - } - - var errs *packersdk.MultiError - var warns []string - - // Defaults - err = b.config.ClientConfig.SetDefaultValues() - if err != nil { - return nil, nil, err - } - - if b.config.ChrootMounts == nil { - b.config.ChrootMounts = make([][]string, 0) - } - - if len(b.config.ChrootMounts) == 0 { - b.config.ChrootMounts = [][]string{ - {"proc", "proc", "/proc"}, - {"sysfs", "sysfs", "/sys"}, - {"bind", "/dev", "/dev"}, - {"devpts", "devpts", "/dev/pts"}, - {"binfmt_misc", "binfmt_misc", "/proc/sys/fs/binfmt_misc"}, - } - } - - // set default copy file if we're not giving our own - if b.config.CopyFiles == nil { - if !b.config.FromScratch { - b.config.CopyFiles = []string{"/etc/resolv.conf"} - } - } - - if b.config.CommandWrapper == "" { - b.config.CommandWrapper = "{{.Command}}" - } - - if b.config.MountPath == "" { - b.config.MountPath = "/mnt/packer-azure-chroot-disks/{{.Device}}" - } - - if b.config.MountPartition == "" { - b.config.MountPartition = "1" - } - - if b.config.TemporaryOSDiskID == "" { - if def, err := interpolate.Render( - "/subscriptions/{{ vm `subscription_id` }}/resourceGroups/{{ vm `resource_group` }}/providers/Microsoft.Compute/disks/PackerTemp-osdisk-{{timestamp}}", - &b.config.ctx); err == nil { - b.config.TemporaryOSDiskID = def - } else { - errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("unable to render temporary disk id: %s", err)) - } - } - - if b.config.TemporaryOSDiskSnapshotID == "" { - if def, err := interpolate.Render( - "/subscriptions/{{ vm `subscription_id` }}/resourceGroups/{{ vm `resource_group` }}/providers/Microsoft.Compute/snapshots/PackerTemp-osdisk-snapshot-{{timestamp}}", - &b.config.ctx); err == nil { - b.config.TemporaryOSDiskSnapshotID = def - } else { - errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("unable to render temporary snapshot id: %s", err)) - } - } - - if b.config.TemporaryDataDiskIDPrefix == "" { - if def, err := interpolate.Render( - "/subscriptions/{{ vm `subscription_id` }}/resourceGroups/{{ vm `resource_group` }}/providers/Microsoft.Compute/disks/PackerTemp-datadisk-{{timestamp}}-", - &b.config.ctx); err == nil { - b.config.TemporaryDataDiskIDPrefix = def - } else { - errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("unable to render temporary data disk id prefix: %s", err)) - } - } - - if b.config.TemporaryDataDiskSnapshotIDPrefix == "" { - if def, err := interpolate.Render( - "/subscriptions/{{ vm `subscription_id` }}/resourceGroups/{{ vm `resource_group` }}/providers/Microsoft.Compute/snapshots/PackerTemp-datadisk-snapshot-{{timestamp}}-", - &b.config.ctx); err == nil { - b.config.TemporaryDataDiskSnapshotIDPrefix = def - } else { - errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("unable to render temporary data disk snapshot id prefix: %s", err)) - } - } - - if b.config.OSDiskStorageAccountType == "" { - b.config.OSDiskStorageAccountType = string(compute.PremiumLRS) - } - - if b.config.OSDiskCacheType == "" { - b.config.OSDiskCacheType = string(compute.CachingTypesReadOnly) - } - - if b.config.DataDiskStorageAccountType == "" { - b.config.DataDiskStorageAccountType = string(compute.PremiumLRS) - } - - if b.config.DataDiskCacheType == "" { - b.config.DataDiskCacheType = string(compute.CachingTypesReadOnly) - } - - if b.config.ImageHyperVGeneration == "" { - b.config.ImageHyperVGeneration = string(compute.V1) - } - - // checks, accumulate any errors or warnings - - if b.config.FromScratch { - if b.config.Source != "" { - errs = packersdk.MultiErrorAppend( - errs, errors.New("source cannot be specified when building from_scratch")) - } - if b.config.OSDiskSizeGB == 0 { - errs = packersdk.MultiErrorAppend( - errs, errors.New("os_disk_size_gb is required with from_scratch")) - } - if len(b.config.PreMountCommands) == 0 { - errs = packersdk.MultiErrorAppend( - errs, errors.New("pre_mount_commands is required with from_scratch")) - } - } else { - if _, err := client.ParsePlatformImageURN(b.config.Source); err == nil { - log.Println("Source is platform image:", b.config.Source) - b.config.sourceType = sourcePlatformImage - } else if id, err := client.ParseResourceID(b.config.Source); err == nil && - strings.EqualFold(id.Provider, "Microsoft.Compute") && - strings.EqualFold(id.ResourceType.String(), "disks") { - log.Println("Source is a disk resource ID:", b.config.Source) - b.config.sourceType = sourceDisk - } else if id, err := client.ParseResourceID(b.config.Source); err == nil && - strings.EqualFold(id.Provider, "Microsoft.Compute") && - strings.EqualFold(id.ResourceType.String(), "galleries/images/versions") { - log.Println("Source is a shared image ID:", b.config.Source) - b.config.sourceType = sourceSharedImage - } else { - errs = packersdk.MultiErrorAppend( - errs, fmt.Errorf("source: %q is not a valid platform image specifier, nor is it a disk resource ID", b.config.Source)) - } - } - - if err := checkDiskCacheType(b.config.OSDiskCacheType); err != nil { - errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("os_disk_cache_type: %v", err)) - } - - if err := checkStorageAccountType(b.config.OSDiskStorageAccountType); err != nil { - errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("os_disk_storage_account_type: %v", err)) - } - - if err := checkDiskCacheType(b.config.DataDiskCacheType); err != nil { - errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("data_disk_cache_type: %v", err)) - } - - if err := checkStorageAccountType(b.config.DataDiskStorageAccountType); err != nil { - errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("data_disk_storage_account_type: %v", err)) - } - - if b.config.ImageResourceID != "" { - r, err := azure.ParseResourceID(b.config.ImageResourceID) - if err != nil || - !strings.EqualFold(r.Provider, "Microsoft.Compute") || - !strings.EqualFold(r.ResourceType, "images") { - errs = packersdk.MultiErrorAppend(fmt.Errorf( - "image_resource_id: %q is not a valid image resource id", b.config.ImageResourceID)) - } - } - - if azcommon.StringsContains(md.Keys, "shared_image_destination") { - e, w := b.config.SharedImageGalleryDestination.Validate("shared_image_destination") - if len(e) > 0 { - errs = packersdk.MultiErrorAppend(errs, e...) - } - if len(w) > 0 { - warns = append(warns, w...) - } - } - - if !azcommon.StringsContains(md.Keys, "shared_image_destination") && b.config.ImageResourceID == "" { - errs = packersdk.MultiErrorAppend(errs, errors.New("image_resource_id or shared_image_destination is required")) - } - - if err := checkHyperVGeneration(b.config.ImageHyperVGeneration); err != nil { - errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("image_hyperv_generation: %v", err)) - } - - if errs != nil { - return nil, warns, errs - } - - packersdk.LogSecretFilter.Set(b.config.ClientConfig.ClientSecret, b.config.ClientConfig.ClientJWT) - return nil, warns, nil -} - -func checkDiskCacheType(s string) interface{} { - for _, v := range compute.PossibleCachingTypesValues() { - if compute.CachingTypes(s) == v { - return nil - } - } - return fmt.Errorf("%q is not a valid value %v", - s, compute.PossibleCachingTypesValues()) -} - -func checkStorageAccountType(s string) interface{} { - for _, v := range compute.PossibleDiskStorageAccountTypesValues() { - if compute.DiskStorageAccountTypes(s) == v { - return nil - } - } - return fmt.Errorf("%q is not a valid value %v", - s, compute.PossibleDiskStorageAccountTypesValues()) -} - -func checkHyperVGeneration(s string) interface{} { - for _, v := range compute.PossibleHyperVGenerationValues() { - if compute.HyperVGeneration(s) == v { - return nil - } - } - return fmt.Errorf("%q is not a valid value %v", - s, compute.PossibleHyperVGenerationValues()) -} - -func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) (packersdk.Artifact, error) { - switch runtime.GOOS { - case "linux", "freebsd": - break - default: - return nil, errors.New("the azure-chroot builder only works on Linux and FreeBSD environments") - } - - err := b.config.ClientConfig.FillParameters() - if err != nil { - return nil, fmt.Errorf("error setting Azure client defaults: %v", err) - } - azcli, err := client.New(b.config.ClientConfig, ui.Say) - if err != nil { - return nil, fmt.Errorf("error creating Azure client: %v", err) - } - - wrappedCommand := func(command string) (string, error) { - ictx := b.config.ctx - ictx.Data = &struct{ Command string }{Command: command} - return interpolate.Render(b.config.CommandWrapper, &ictx) - } - - // Setup the state bag and initial state for the steps - state := new(multistep.BasicStateBag) - state.Put("config", &b.config) - state.Put("hook", hook) - state.Put("ui", ui) - state.Put("azureclient", azcli) - state.Put("wrappedCommand", common.CommandWrapper(wrappedCommand)) - - info, err := azcli.MetadataClient().GetComputeInfo() - if err != nil { - log.Printf("MetadataClient().GetComputeInfo(): error: %+v", err) - err := fmt.Errorf( - "Error retrieving information ARM resource ID and location" + - "of the VM that Packer is running on.\n" + - "Please verify that Packer is running on a proper Azure VM.") - ui.Error(err.Error()) - return nil, err - } - - state.Put("instance", info) - - // Build the step array from the config - steps := buildsteps(b.config, info) - - // Run! - b.runner = commonsteps.NewRunner(steps, b.config.PackerConfig, ui) - b.runner.Run(ctx, state) - - // If there was an error, return that - if rawErr, ok := state.GetOk("error"); ok { - return nil, rawErr.(error) - } - - // Build the artifact and return it - artifact := &azcommon.Artifact{ - BuilderIdValue: BuilderID, - StateData: map[string]interface{}{"generated_data": state.Get("generated_data")}, - AzureClientSet: azcli, - } - if b.config.ImageResourceID != "" { - artifact.Resources = append(artifact.Resources, b.config.ImageResourceID) - } - if e, _ := b.config.SharedImageGalleryDestination.Validate(""); len(e) == 0 { - artifact.Resources = append(artifact.Resources, b.config.SharedImageGalleryDestination.ResourceID(info.SubscriptionID)) - } - if b.config.SkipCleanup { - if d, ok := state.GetOk(stateBagKey_Diskset); ok { - for _, disk := range d.(Diskset) { - artifact.Resources = append(artifact.Resources, disk.String()) - } - } - if d, ok := state.GetOk(stateBagKey_Snapshotset); ok { - for _, snapshot := range d.(Diskset) { - artifact.Resources = append(artifact.Resources, snapshot.String()) - } - } - } - - return artifact, nil -} - -func buildsteps(config Config, info *client.ComputeInfo) []multistep.Step { - // Build the steps - var steps []multistep.Step - addSteps := func(s ...multistep.Step) { // convenience function - steps = append(steps, s...) - } - - e, _ := config.SharedImageGalleryDestination.Validate("") - hasValidSharedImage := len(e) == 0 - - if hasValidSharedImage { - // validate destination early - addSteps( - &StepVerifySharedImageDestination{ - Image: config.SharedImageGalleryDestination, - Location: info.Location, - }, - ) - } - - if config.FromScratch { - addSteps(&StepCreateNewDiskset{ - OSDiskID: config.TemporaryOSDiskID, - OSDiskSizeGB: config.OSDiskSizeGB, - OSDiskStorageAccountType: config.OSDiskStorageAccountType, - HyperVGeneration: config.ImageHyperVGeneration, - Location: info.Location}) - } else { - switch config.sourceType { - case sourcePlatformImage: - if pi, err := client.ParsePlatformImageURN(config.Source); err == nil { - if strings.EqualFold(pi.Version, "latest") { - addSteps( - &StepResolvePlatformImageVersion{ - PlatformImage: pi, - Location: info.Location, - }) - } - addSteps( - &StepCreateNewDiskset{ - OSDiskID: config.TemporaryOSDiskID, - OSDiskSizeGB: config.OSDiskSizeGB, - OSDiskStorageAccountType: config.OSDiskStorageAccountType, - HyperVGeneration: config.ImageHyperVGeneration, - Location: info.Location, - SourcePlatformImage: pi, - - SkipCleanup: config.SkipCleanup, - }) - } else { - panic("Couldn't parse platfrom image urn: " + config.Source + " err: " + err.Error()) - } - - case sourceDisk: - addSteps( - &StepVerifySourceDisk{ - SourceDiskResourceID: config.Source, - Location: info.Location, - }, - &StepCreateNewDiskset{ - OSDiskID: config.TemporaryOSDiskID, - OSDiskSizeGB: config.OSDiskSizeGB, - OSDiskStorageAccountType: config.OSDiskStorageAccountType, - HyperVGeneration: config.ImageHyperVGeneration, - SourceOSDiskResourceID: config.Source, - Location: info.Location, - - SkipCleanup: config.SkipCleanup, - }) - - case sourceSharedImage: - addSteps( - &StepVerifySharedImageSource{ - SharedImageID: config.Source, - SubscriptionID: info.SubscriptionID, - Location: info.Location, - }, - &StepCreateNewDiskset{ - OSDiskID: config.TemporaryOSDiskID, - DataDiskIDPrefix: config.TemporaryDataDiskIDPrefix, - OSDiskSizeGB: config.OSDiskSizeGB, - OSDiskStorageAccountType: config.OSDiskStorageAccountType, - DataDiskStorageAccountType: config.DataDiskStorageAccountType, - SourceImageResourceID: config.Source, - Location: info.Location, - - SkipCleanup: config.SkipCleanup, - }) - - default: - panic(fmt.Errorf("Unknown source type: %+q", config.sourceType)) - } - } - - addSteps( - &StepAttachDisk{}, // uses os_disk_resource_id and sets 'device' in stateBag - &chroot.StepPreMountCommands{ - Commands: config.PreMountCommands, - }, - &StepMountDevice{ - MountOptions: config.MountOptions, - MountPartition: config.MountPartition, - MountPath: config.MountPath, - }, - &chroot.StepPostMountCommands{ - Commands: config.PostMountCommands, - }, - &chroot.StepMountExtra{ - ChrootMounts: config.ChrootMounts, - }, - &chroot.StepCopyFiles{ - Files: config.CopyFiles, - }, - &chroot.StepChrootProvision{}, - &chroot.StepEarlyCleanup{}, - ) - - if config.ImageResourceID != "" { - addSteps(&StepCreateImage{ - ImageResourceID: config.ImageResourceID, - ImageOSState: string(compute.Generalized), - OSDiskCacheType: config.OSDiskCacheType, - OSDiskStorageAccountType: config.OSDiskStorageAccountType, - Location: info.Location, - }) - } - if hasValidSharedImage { - addSteps( - &StepCreateSnapshotset{ - OSDiskSnapshotID: config.TemporaryOSDiskSnapshotID, - DataDiskSnapshotIDPrefix: config.TemporaryDataDiskSnapshotIDPrefix, - Location: info.Location, - SkipCleanup: config.SkipCleanup, - }, - &StepCreateSharedImageVersion{ - Destination: config.SharedImageGalleryDestination, - OSDiskCacheType: config.OSDiskCacheType, - Location: info.Location, - }, - ) - } - - return steps -} diff --git a/builder/azure/chroot/builder.hcl2spec.go b/builder/azure/chroot/builder.hcl2spec.go deleted file mode 100644 index 96f4c1ae9..000000000 --- a/builder/azure/chroot/builder.hcl2spec.go +++ /dev/null @@ -1,111 +0,0 @@ -// Code generated by "packer-sdc mapstructure-to-hcl2"; DO NOT EDIT. - -package chroot - -import ( - "github.com/hashicorp/hcl/v2/hcldec" - "github.com/zclconf/go-cty/cty" -) - -// FlatConfig is an auto-generated flat version of Config. -// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. -type FlatConfig struct { - PackerBuildName *string `mapstructure:"packer_build_name" cty:"packer_build_name" hcl:"packer_build_name"` - PackerBuilderType *string `mapstructure:"packer_builder_type" cty:"packer_builder_type" hcl:"packer_builder_type"` - PackerCoreVersion *string `mapstructure:"packer_core_version" cty:"packer_core_version" hcl:"packer_core_version"` - PackerDebug *bool `mapstructure:"packer_debug" cty:"packer_debug" hcl:"packer_debug"` - PackerForce *bool `mapstructure:"packer_force" cty:"packer_force" hcl:"packer_force"` - PackerOnError *string `mapstructure:"packer_on_error" cty:"packer_on_error" hcl:"packer_on_error"` - PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables" hcl:"packer_user_variables"` - PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables" hcl:"packer_sensitive_variables"` - CloudEnvironmentName *string `mapstructure:"cloud_environment_name" required:"false" cty:"cloud_environment_name" hcl:"cloud_environment_name"` - ClientID *string `mapstructure:"client_id" cty:"client_id" hcl:"client_id"` - ClientSecret *string `mapstructure:"client_secret" cty:"client_secret" hcl:"client_secret"` - ClientCertPath *string `mapstructure:"client_cert_path" cty:"client_cert_path" hcl:"client_cert_path"` - ClientCertExpireTimeout *string `mapstructure:"client_cert_token_timeout" required:"false" cty:"client_cert_token_timeout" hcl:"client_cert_token_timeout"` - ClientJWT *string `mapstructure:"client_jwt" cty:"client_jwt" hcl:"client_jwt"` - ObjectID *string `mapstructure:"object_id" cty:"object_id" hcl:"object_id"` - TenantID *string `mapstructure:"tenant_id" required:"false" cty:"tenant_id" hcl:"tenant_id"` - SubscriptionID *string `mapstructure:"subscription_id" cty:"subscription_id" hcl:"subscription_id"` - UseAzureCLIAuth *bool `mapstructure:"use_azure_cli_auth" required:"false" cty:"use_azure_cli_auth" hcl:"use_azure_cli_auth"` - FromScratch *bool `mapstructure:"from_scratch" cty:"from_scratch" hcl:"from_scratch"` - Source *string `mapstructure:"source" required:"true" cty:"source" hcl:"source"` - CommandWrapper *string `mapstructure:"command_wrapper" cty:"command_wrapper" hcl:"command_wrapper"` - PreMountCommands []string `mapstructure:"pre_mount_commands" cty:"pre_mount_commands" hcl:"pre_mount_commands"` - MountOptions []string `mapstructure:"mount_options" cty:"mount_options" hcl:"mount_options"` - MountPartition *string `mapstructure:"mount_partition" cty:"mount_partition" hcl:"mount_partition"` - MountPath *string `mapstructure:"mount_path" cty:"mount_path" hcl:"mount_path"` - PostMountCommands []string `mapstructure:"post_mount_commands" cty:"post_mount_commands" hcl:"post_mount_commands"` - ChrootMounts [][]string `mapstructure:"chroot_mounts" cty:"chroot_mounts" hcl:"chroot_mounts"` - CopyFiles []string `mapstructure:"copy_files" cty:"copy_files" hcl:"copy_files"` - OSDiskSizeGB *int32 `mapstructure:"os_disk_size_gb" cty:"os_disk_size_gb" hcl:"os_disk_size_gb"` - OSDiskStorageAccountType *string `mapstructure:"os_disk_storage_account_type" cty:"os_disk_storage_account_type" hcl:"os_disk_storage_account_type"` - OSDiskCacheType *string `mapstructure:"os_disk_cache_type" cty:"os_disk_cache_type" hcl:"os_disk_cache_type"` - DataDiskStorageAccountType *string `mapstructure:"data_disk_storage_account_type" cty:"data_disk_storage_account_type" hcl:"data_disk_storage_account_type"` - DataDiskCacheType *string `mapstructure:"data_disk_cache_type" cty:"data_disk_cache_type" hcl:"data_disk_cache_type"` - ImageHyperVGeneration *string `mapstructure:"image_hyperv_generation" cty:"image_hyperv_generation" hcl:"image_hyperv_generation"` - TemporaryOSDiskID *string `mapstructure:"temporary_os_disk_id" cty:"temporary_os_disk_id" hcl:"temporary_os_disk_id"` - TemporaryOSDiskSnapshotID *string `mapstructure:"temporary_os_disk_snapshot_id" cty:"temporary_os_disk_snapshot_id" hcl:"temporary_os_disk_snapshot_id"` - TemporaryDataDiskIDPrefix *string `mapstructure:"temporary_data_disk_id_prefix" cty:"temporary_data_disk_id_prefix" hcl:"temporary_data_disk_id_prefix"` - TemporaryDataDiskSnapshotIDPrefix *string `mapstructure:"temporary_data_disk_snapshot_id" cty:"temporary_data_disk_snapshot_id" hcl:"temporary_data_disk_snapshot_id"` - SkipCleanup *bool `mapstructure:"skip_cleanup" cty:"skip_cleanup" hcl:"skip_cleanup"` - ImageResourceID *string `mapstructure:"image_resource_id" cty:"image_resource_id" hcl:"image_resource_id"` - SharedImageGalleryDestination *FlatSharedImageGalleryDestination `mapstructure:"shared_image_destination" cty:"shared_image_destination" hcl:"shared_image_destination"` -} - -// FlatMapstructure returns a new FlatConfig. -// FlatConfig is an auto-generated flat version of Config. -// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { - return new(FlatConfig) -} - -// HCL2Spec returns the hcl spec of a Config. -// This spec is used by HCL to read the fields of Config. -// The decoded values from this spec will then be applied to a FlatConfig. -func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { - s := map[string]hcldec.Spec{ - "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, - "packer_builder_type": &hcldec.AttrSpec{Name: "packer_builder_type", Type: cty.String, Required: false}, - "packer_core_version": &hcldec.AttrSpec{Name: "packer_core_version", Type: cty.String, Required: false}, - "packer_debug": &hcldec.AttrSpec{Name: "packer_debug", Type: cty.Bool, Required: false}, - "packer_force": &hcldec.AttrSpec{Name: "packer_force", Type: cty.Bool, Required: false}, - "packer_on_error": &hcldec.AttrSpec{Name: "packer_on_error", Type: cty.String, Required: false}, - "packer_user_variables": &hcldec.AttrSpec{Name: "packer_user_variables", Type: cty.Map(cty.String), Required: false}, - "packer_sensitive_variables": &hcldec.AttrSpec{Name: "packer_sensitive_variables", Type: cty.List(cty.String), Required: false}, - "cloud_environment_name": &hcldec.AttrSpec{Name: "cloud_environment_name", Type: cty.String, Required: false}, - "client_id": &hcldec.AttrSpec{Name: "client_id", Type: cty.String, Required: false}, - "client_secret": &hcldec.AttrSpec{Name: "client_secret", Type: cty.String, Required: false}, - "client_cert_path": &hcldec.AttrSpec{Name: "client_cert_path", Type: cty.String, Required: false}, - "client_cert_token_timeout": &hcldec.AttrSpec{Name: "client_cert_token_timeout", Type: cty.String, Required: false}, - "client_jwt": &hcldec.AttrSpec{Name: "client_jwt", Type: cty.String, Required: false}, - "object_id": &hcldec.AttrSpec{Name: "object_id", Type: cty.String, Required: false}, - "tenant_id": &hcldec.AttrSpec{Name: "tenant_id", Type: cty.String, Required: false}, - "subscription_id": &hcldec.AttrSpec{Name: "subscription_id", Type: cty.String, Required: false}, - "use_azure_cli_auth": &hcldec.AttrSpec{Name: "use_azure_cli_auth", Type: cty.Bool, Required: false}, - "from_scratch": &hcldec.AttrSpec{Name: "from_scratch", Type: cty.Bool, Required: false}, - "source": &hcldec.AttrSpec{Name: "source", Type: cty.String, Required: false}, - "command_wrapper": &hcldec.AttrSpec{Name: "command_wrapper", Type: cty.String, Required: false}, - "pre_mount_commands": &hcldec.AttrSpec{Name: "pre_mount_commands", Type: cty.List(cty.String), Required: false}, - "mount_options": &hcldec.AttrSpec{Name: "mount_options", Type: cty.List(cty.String), Required: false}, - "mount_partition": &hcldec.AttrSpec{Name: "mount_partition", Type: cty.String, Required: false}, - "mount_path": &hcldec.AttrSpec{Name: "mount_path", Type: cty.String, Required: false}, - "post_mount_commands": &hcldec.AttrSpec{Name: "post_mount_commands", Type: cty.List(cty.String), Required: false}, - "chroot_mounts": &hcldec.AttrSpec{Name: "chroot_mounts", Type: cty.List(cty.List(cty.String)), Required: false}, - "copy_files": &hcldec.AttrSpec{Name: "copy_files", Type: cty.List(cty.String), Required: false}, - "os_disk_size_gb": &hcldec.AttrSpec{Name: "os_disk_size_gb", Type: cty.Number, Required: false}, - "os_disk_storage_account_type": &hcldec.AttrSpec{Name: "os_disk_storage_account_type", Type: cty.String, Required: false}, - "os_disk_cache_type": &hcldec.AttrSpec{Name: "os_disk_cache_type", Type: cty.String, Required: false}, - "data_disk_storage_account_type": &hcldec.AttrSpec{Name: "data_disk_storage_account_type", Type: cty.String, Required: false}, - "data_disk_cache_type": &hcldec.AttrSpec{Name: "data_disk_cache_type", Type: cty.String, Required: false}, - "image_hyperv_generation": &hcldec.AttrSpec{Name: "image_hyperv_generation", Type: cty.String, Required: false}, - "temporary_os_disk_id": &hcldec.AttrSpec{Name: "temporary_os_disk_id", Type: cty.String, Required: false}, - "temporary_os_disk_snapshot_id": &hcldec.AttrSpec{Name: "temporary_os_disk_snapshot_id", Type: cty.String, Required: false}, - "temporary_data_disk_id_prefix": &hcldec.AttrSpec{Name: "temporary_data_disk_id_prefix", Type: cty.String, Required: false}, - "temporary_data_disk_snapshot_id": &hcldec.AttrSpec{Name: "temporary_data_disk_snapshot_id", Type: cty.String, Required: false}, - "skip_cleanup": &hcldec.AttrSpec{Name: "skip_cleanup", Type: cty.Bool, Required: false}, - "image_resource_id": &hcldec.AttrSpec{Name: "image_resource_id", Type: cty.String, Required: false}, - "shared_image_destination": &hcldec.BlockSpec{TypeName: "shared_image_destination", Nested: hcldec.ObjectSpec((*FlatSharedImageGalleryDestination)(nil).HCL2Spec())}, - } - return s -} diff --git a/builder/azure/chroot/builder_test.go b/builder/azure/chroot/builder_test.go deleted file mode 100644 index 88a38cc8e..000000000 --- a/builder/azure/chroot/builder_test.go +++ /dev/null @@ -1,234 +0,0 @@ -package chroot - -import ( - "strings" - "testing" - - "github.com/hashicorp/packer-plugin-sdk/multistep" - "github.com/hashicorp/packer/builder/azure/common/client" - - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-12-01/compute" -) - -func TestBuilder_Prepare(t *testing.T) { - type config map[string]interface{} - type regexMatchers map[string]string // map of regex : error message - - tests := []struct { - name string - config config - validate func(Config) - wantErr bool - }{ - { - name: "platform image to managed disk", - config: config{ - "client_id": "123", - "client_secret": "456", - "subscription_id": "789", - "source": "credativ:Debian:9:latest", - "image_resource_id": "/subscriptions/789/resourceGroups/otherrgname/providers/Microsoft.Compute/images/MyDebianOSImage-{{timestamp}}", - "shared_image_destination": config{ - "resource_group": "otherrgname", - "gallery_name": "myGallery", - "image_name": "imageName", - "image_version": "1.0.2", - }, - }, - validate: func(c Config) { - if c.OSDiskSizeGB != 0 { - t.Errorf("Expected OSDiskSizeGB to be 0, was %+v", c.OSDiskSizeGB) - } - if c.MountPartition != "1" { - t.Errorf("Expected MountPartition to be %s, but found %s", "1", c.MountPartition) - } - if c.OSDiskStorageAccountType != string(compute.PremiumLRS) { - t.Errorf("Expected OSDiskStorageAccountType to be %s, but found %s", string(compute.PremiumLRS), c.OSDiskStorageAccountType) - } - if c.OSDiskCacheType != string(compute.CachingTypesReadOnly) { - t.Errorf("Expected OSDiskCacheType to be %s, but found %s", string(compute.CachingTypesReadOnly), c.OSDiskCacheType) - } - if c.ImageHyperVGeneration != string(compute.V1) { - t.Errorf("Expected ImageHyperVGeneration to be %s, but found %s", string(compute.V1), c.ImageHyperVGeneration) - } - }, - }, - { - name: "disk to managed image, validate temp disk id expansion", - config: config{ - "source": "/subscriptions/789/resourceGroups/testrg/providers/Microsoft.Compute/disks/diskname", - "image_resource_id": "/subscriptions/789/resourceGroups/otherrgname/providers/Microsoft.Compute/images/MyDebianOSImage-{{timestamp}}", - }, - validate: func(c Config) { - prefix := "/subscriptions/testSubscriptionID/resourceGroups/testResourceGroup/providers/Microsoft.Compute/disks/PackerTemp-osdisk-" - if !strings.HasPrefix(c.TemporaryOSDiskID, prefix) { - t.Errorf("Expected TemporaryOSDiskID to start with %q, but got %q", prefix, c.TemporaryOSDiskID) - } - }, - }, - { - name: "disk to both managed image and shared image", - config: config{ - "source": "/subscriptions/789/resourceGroups/testrg/providers/Microsoft.Compute/disks/diskname", - "image_resource_id": "/subscriptions/789/resourceGroups/otherrgname/providers/Microsoft.Compute/images/MyDebianOSImage-{{timestamp}}", - "shared_image_destination": config{ - "resource_group": "rg", - "gallery_name": "galleryName", - "image_name": "imageName", - "image_version": "0.1.0", - }, - }, - }, - { - name: "disk to both managed image and shared image with missing property", - config: config{ - "source": "/subscriptions/789/resourceGroups/testrg/providers/Microsoft.Compute/disks/diskname", - "image_resource_id": "/subscriptions/789/resourceGroups/otherrgname/providers/Microsoft.Compute/images/MyDebianOSImage-{{timestamp}}", - "shared_image_destination": config{ - "resource_group": "rg", - "gallery_name": "galleryName", - "image_version": "0.1.0", - }, - }, - wantErr: true, - }, - { - name: "from shared image", - config: config{ - "shared_image_destination": config{ - "resource_group": "otherrgname", - "gallery_name": "myGallery", - "image_name": "imageName", - "image_version": "1.0.2", - }, - "source": "/subscriptions/789/resourceGroups/testrg/providers/Microsoft.Compute/disks/diskname", - }, - wantErr: false, - }, - { - name: "err: no output", - config: config{ - "source": "/subscriptions/789/resourceGroups/testrg/providers/Microsoft.Compute/disks/diskname", - }, - wantErr: true, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - withMetadataStub(func() { - b := &Builder{} - - _, _, err := b.Prepare(tt.config) - - if (err != nil) != tt.wantErr { - t.Errorf("Builder.Prepare() error = %v, wantErr %v", err, tt.wantErr) - return - } - - if tt.validate != nil { - tt.validate(b.config) - } - }) - }) - } -} - -func Test_buildsteps(t *testing.T) { - info := &client.ComputeInfo{ - Location: "northpole", - Name: "unittestVM", - ResourceGroupName: "unittestResourceGroup", - SubscriptionID: "96854241-60c7-426d-9a27-3fdeec8957f4", - } - - tests := []struct { - name string - config Config - verify func([]multistep.Step, *testing.T) - }{ - { - name: "Source FromScrath creates empty disk", - config: Config{FromScratch: true}, - verify: func(steps []multistep.Step, _ *testing.T) { - for _, s := range steps { - if s, ok := s.(*StepCreateNewDiskset); ok { - if s.SourceOSDiskResourceID == "" && - s.SourcePlatformImage == nil { - return - } - t.Errorf("found misconfigured StepCreateNewDisk: %+v", s) - } - } - t.Error("did not find a StepCreateNewDisk") - }}, - { - name: "Source Platform image disk creation", - config: Config{Source: "publisher:offer:sku:version", sourceType: sourcePlatformImage}, - verify: func(steps []multistep.Step, _ *testing.T) { - for _, s := range steps { - if s, ok := s.(*StepCreateNewDiskset); ok { - if s.SourceOSDiskResourceID == "" && - s.SourcePlatformImage != nil && - s.SourcePlatformImage.Publisher == "publisher" { - return - } - t.Errorf("found misconfigured StepCreateNewDisk: %+v", s) - } - } - t.Error("did not find a StepCreateNewDisk") - }}, - { - name: "Source Platform image with version latest adds StepResolvePlatformImageVersion", - config: Config{Source: "publisher:offer:sku:latest", sourceType: sourcePlatformImage}, - verify: func(steps []multistep.Step, _ *testing.T) { - for _, s := range steps { - if s, ok := s.(*StepResolvePlatformImageVersion); ok { - if s.PlatformImage != nil && - s.Location == info.Location { - return - } - t.Errorf("found misconfigured StepResolvePlatformImageVersion: %+v", s) - } - } - t.Error("did not find a StepResolvePlatformImageVersion") - }}, - { - name: "Source Disk adds correct disk creation", - config: Config{Source: "diskresourceid", sourceType: sourceDisk}, - verify: func(steps []multistep.Step, _ *testing.T) { - for _, s := range steps { - if s, ok := s.(*StepCreateNewDiskset); ok { - if s.SourceOSDiskResourceID == "diskresourceid" && - s.SourcePlatformImage == nil { - return - } - t.Errorf("found misconfigured StepCreateNewDisk: %+v", s) - } - } - t.Error("did not find a StepCreateNewDisk") - }}, - { - name: "Source disk adds StepVerifySourceDisk", - config: Config{Source: "diskresourceid", sourceType: sourceDisk}, - verify: func(steps []multistep.Step, _ *testing.T) { - for _, s := range steps { - if s, ok := s.(*StepVerifySourceDisk); ok { - if s.SourceDiskResourceID == "diskresourceid" && - s.Location == info.Location { - return - } - t.Errorf("found misconfigured StepVerifySourceDisk: %+v", s) - } - } - t.Error("did not find a StepVerifySourceDisk") - }}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - withMetadataStub(func() { // ensure that values are taken from info, instead of retrieved again - got := buildsteps(tt.config, info) - tt.verify(got, t) - }) - }) - } -} diff --git a/builder/azure/chroot/const.go b/builder/azure/chroot/const.go deleted file mode 100644 index e99cf6284..000000000 --- a/builder/azure/chroot/const.go +++ /dev/null @@ -1,6 +0,0 @@ -package chroot - -const ( - stateBagKey_Diskset = "diskset" - stateBagKey_Snapshotset = "snapshotset" -) diff --git a/builder/azure/chroot/diskattacher.go b/builder/azure/chroot/diskattacher.go deleted file mode 100644 index a6516e3e3..000000000 --- a/builder/azure/chroot/diskattacher.go +++ /dev/null @@ -1,190 +0,0 @@ -package chroot - -import ( - "context" - "errors" - "log" - "strings" - "time" - - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-12-01/compute" - "github.com/Azure/go-autorest/autorest/azure" - "github.com/Azure/go-autorest/autorest/to" - "github.com/hashicorp/packer/builder/azure/common/client" -) - -type DiskAttacher interface { - AttachDisk(ctx context.Context, disk string) (lun int32, err error) - WaitForDevice(ctx context.Context, i int32) (device string, err error) - DetachDisk(ctx context.Context, disk string) (err error) - WaitForDetach(ctx context.Context, diskID string) error -} - -var NewDiskAttacher = func(azureClient client.AzureClientSet) DiskAttacher { - return &diskAttacher{ - azcli: azureClient, - } -} - -type diskAttacher struct { - azcli client.AzureClientSet - - vm *client.ComputeInfo // store info about this VM so that we don't have to ask metadata service on every call -} - -var DiskNotFoundError = errors.New("Disk not found") - -func (da *diskAttacher) DetachDisk(ctx context.Context, diskID string) error { - log.Println("Fetching list of disks currently attached to VM") - currentDisks, err := da.getDisks(ctx) - if err != nil { - return err - } - - log.Printf("Removing %q from list of disks currently attached to VM", diskID) - newDisks := []compute.DataDisk{} - for _, disk := range currentDisks { - if disk.ManagedDisk != nil && - !strings.EqualFold(to.String(disk.ManagedDisk.ID), diskID) { - newDisks = append(newDisks, disk) - } - } - if len(currentDisks) == len(newDisks) { - return DiskNotFoundError - } - - log.Println("Updating new list of disks attached to VM") - err = da.setDisks(ctx, newDisks) - if err != nil { - return err - } - - return nil -} - -func (da *diskAttacher) WaitForDetach(ctx context.Context, diskID string) error { - for { // loop until disk is not attached, timeout or error - list, err := da.getDisks(ctx) - if err != nil { - return err - } - if findDiskInList(list, diskID) == nil { - log.Println("Disk is no longer in VM model, assuming detached") - return nil - } - - select { - case <-time.After(time.Second): //continue - case <-ctx.Done(): - return ctx.Err() - } - } -} - -func (da *diskAttacher) AttachDisk(ctx context.Context, diskID string) (int32, error) { - dataDisks, err := da.getDisks(ctx) - if err != nil { - return -1, err - } - - // check to see if disk is already attached, remember lun if found - if disk := findDiskInList(dataDisks, diskID); disk != nil { - // disk is already attached, just take this lun - if disk.Lun == nil { - return -1, errors.New("disk is attached, but lun was not set in VM model (possibly an error in the Azure APIs)") - } - return to.Int32(disk.Lun), nil - } - - // disk was not found on VM, go and actually attach it - - var lun int32 = -1 -findFreeLun: - for lun = 0; lun < 64; lun++ { - for _, v := range dataDisks { - if to.Int32(v.Lun) == lun { - continue findFreeLun - } - } - // no datadisk is using this lun - break - } - - // append new data disk to collection - dataDisks = append(dataDisks, compute.DataDisk{ - CreateOption: compute.DiskCreateOptionTypesAttach, - ManagedDisk: &compute.ManagedDiskParameters{ - ID: to.StringPtr(diskID), - }, - Lun: to.Int32Ptr(lun), - }) - - // prepare resource object for update operation - err = da.setDisks(ctx, dataDisks) - if err != nil { - return -1, err - } - - return lun, nil -} - -func (da *diskAttacher) getThisVM(ctx context.Context) (compute.VirtualMachine, error) { - // getting resource info for this VM - if da.vm == nil { - vm, err := da.azcli.MetadataClient().GetComputeInfo() - if err != nil { - return compute.VirtualMachine{}, err - } - da.vm = vm - } - - // retrieve actual VM - vmResource, err := da.azcli.VirtualMachinesClient().Get(ctx, da.vm.ResourceGroupName, da.vm.Name, "") - if err != nil { - return compute.VirtualMachine{}, err - } - if vmResource.StorageProfile == nil { - return compute.VirtualMachine{}, errors.New("properties.storageProfile is not set on VM, this is unexpected") - } - - return vmResource, nil -} - -func (da diskAttacher) getDisks(ctx context.Context) ([]compute.DataDisk, error) { - vmResource, err := da.getThisVM(ctx) - if err != nil { - return []compute.DataDisk{}, err - } - - return *vmResource.StorageProfile.DataDisks, nil -} - -func (da diskAttacher) setDisks(ctx context.Context, disks []compute.DataDisk) error { - vmResource, err := da.getThisVM(ctx) - if err != nil { - return err - } - - id, err := azure.ParseResourceID(to.String(vmResource.ID)) - if err != nil { - return err - } - - vmResource.StorageProfile.DataDisks = &disks - vmResource.Resources = nil - - // update the VM resource, attach disk - _, err = da.azcli.VirtualMachinesClient().CreateOrUpdate(ctx, id.ResourceGroup, id.ResourceName, vmResource) - - return err -} - -func findDiskInList(list []compute.DataDisk, diskID string) *compute.DataDisk { - for _, disk := range list { - if disk.ManagedDisk != nil && - strings.EqualFold(to.String(disk.ManagedDisk.ID), diskID) { - return &disk - } - } - return nil -} diff --git a/builder/azure/chroot/diskattacher_freebsd.go b/builder/azure/chroot/diskattacher_freebsd.go deleted file mode 100644 index ecfc5cf60..000000000 --- a/builder/azure/chroot/diskattacher_freebsd.go +++ /dev/null @@ -1,54 +0,0 @@ -package chroot - -import ( - "bufio" - "bytes" - "context" - "fmt" - "os/exec" - "regexp" - "strings" - "time" -) - -func (da diskAttacher) WaitForDevice(ctx context.Context, lun int32) (device string, err error) { - // This builder will always be running in Azure, where data disks show up - // on scbus5 target 0. The camcontrol command always outputs LUNs in - // unpadded hexadecimal format. - regexStr := fmt.Sprintf(`at scbus5 target 0 lun %x \(.*?da([\d]+)`, lun) - devRegex, err := regexp.Compile(regexStr) - if err != nil { - return "", err - } - for { - cmd := exec.Command("camcontrol", "devlist") - var out bytes.Buffer - cmd.Stdout = &out - err = cmd.Run() - if err != nil { - return "", err - } - outString := out.String() - scanner := bufio.NewScanner(strings.NewReader(outString)) - for scanner.Scan() { - line := scanner.Text() - // Check if this is the correct bus, target, and LUN. - if matches := devRegex.FindStringSubmatch(line); matches != nil { - // If this function immediately returns, devfs won't have - // created the device yet. - time.Sleep(1000 * time.Millisecond) - return fmt.Sprintf("/dev/da%s", matches[1]), nil - } - } - if err = scanner.Err(); err != nil { - return "", err - } - - select { - case <-time.After(100 * time.Millisecond): - // continue - case <-ctx.Done(): - return "", ctx.Err() - } - } -} diff --git a/builder/azure/chroot/diskattacher_linux.go b/builder/azure/chroot/diskattacher_linux.go deleted file mode 100644 index 3d1403691..000000000 --- a/builder/azure/chroot/diskattacher_linux.go +++ /dev/null @@ -1,36 +0,0 @@ -package chroot - -import ( - "context" - "fmt" - "os" - "path/filepath" - "syscall" - "time" -) - -func diskPathForLun(lun int32) string { - return fmt.Sprintf("/dev/disk/azure/scsi1/lun%d", lun) -} - -func (da diskAttacher) WaitForDevice(ctx context.Context, lun int32) (device string, err error) { - path := diskPathForLun(lun) - - for { - link, err := os.Readlink(path) - if err == nil { - return filepath.Abs("/dev/disk/azure/scsi1/" + link) - } else if err != os.ErrNotExist { - if pe, ok := err.(*os.PathError); ok && pe.Err != syscall.ENOENT { - return "", err - } - } - - select { - case <-time.After(100 * time.Millisecond): - // continue - case <-ctx.Done(): - return "", ctx.Err() - } - } -} diff --git a/builder/azure/chroot/diskattacher_other.go b/builder/azure/chroot/diskattacher_other.go deleted file mode 100644 index 3fea09037..000000000 --- a/builder/azure/chroot/diskattacher_other.go +++ /dev/null @@ -1,11 +0,0 @@ -// +build !linux,!freebsd - -package chroot - -import ( - "context" -) - -func (da diskAttacher) WaitForDevice(ctx context.Context, lun int32) (device string, err error) { - panic("The azure-chroot builder does not work on this platform.") -} diff --git a/builder/azure/chroot/diskattacher_test.go b/builder/azure/chroot/diskattacher_test.go deleted file mode 100644 index 9e617f59b..000000000 --- a/builder/azure/chroot/diskattacher_test.go +++ /dev/null @@ -1,87 +0,0 @@ -package chroot - -import ( - "context" - "testing" - - "github.com/Azure/go-autorest/autorest/to" - - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-12-01/compute" - "github.com/hashicorp/packer/builder/azure/common/client" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -const ( - testvm = "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testGroup/Microsoft.Compute/virtualMachines/testVM" - testdisk = "/subscriptions/00000000-0000-0000-0000-000000000001/resourceGroups/testGroup2/Microsoft.Compute/disks/testDisk" -) - -// Tests assume current machine is capable of running chroot builder (i.e. an Azure VM) - -func Test_DiskAttacherAttachesDiskToVM(t *testing.T) { - azcli, err := client.GetTestClientSet(t) // integration test - require.Nil(t, err) - da := NewDiskAttacher(azcli) - testDiskName := t.Name() - - vm, err := azcli.MetadataClient().GetComputeInfo() - require.Nil(t, err, "Test needs to run on an Azure VM, unable to retrieve VM information") - t.Log("Creating new disk '", testDiskName, "' in ", vm.ResourceGroupName) - - disk, err := azcli.DisksClient().Get(context.TODO(), vm.ResourceGroupName, testDiskName) - if err == nil { - t.Log("Disk already exists") - if disk.DiskState == compute.Attached { - t.Log("Disk is attached, assuming to this machine, trying to detach") - err = da.DetachDisk(context.TODO(), to.String(disk.ID)) - require.Nil(t, err) - } - t.Log("Deleting disk") - result, err := azcli.DisksClient().Delete(context.TODO(), vm.ResourceGroupName, testDiskName) - require.Nil(t, err) - err = result.WaitForCompletionRef(context.TODO(), azcli.PollClient()) - require.Nil(t, err) - } - - t.Log("Creating disk") - r, err := azcli.DisksClient().CreateOrUpdate(context.TODO(), vm.ResourceGroupName, testDiskName, compute.Disk{ - Location: to.StringPtr(vm.Location), - Sku: &compute.DiskSku{ - Name: compute.StandardLRS, - }, - DiskProperties: &compute.DiskProperties{ - DiskSizeGB: to.Int32Ptr(30), - CreationData: &compute.CreationData{CreateOption: compute.Empty}, - }, - }) - require.Nil(t, err) - err = r.WaitForCompletionRef(context.TODO(), azcli.PollClient()) - require.Nil(t, err) - - t.Log("Retrieving disk properties") - d, err := azcli.DisksClient().Get(context.TODO(), vm.ResourceGroupName, testDiskName) - require.Nil(t, err) - assert.NotNil(t, d) - - t.Log("Attaching disk") - lun, err := da.AttachDisk(context.TODO(), to.String(d.ID)) - assert.Nil(t, err) - - t.Log("Waiting for device") - dev, err := da.WaitForDevice(context.TODO(), lun) - assert.Nil(t, err) - - t.Log("Device path:", dev) - - t.Log("Detaching disk") - err = da.DetachDisk(context.TODO(), to.String(d.ID)) - require.Nil(t, err) - - t.Log("Deleting disk") - result, err := azcli.DisksClient().Delete(context.TODO(), vm.ResourceGroupName, testDiskName) - if err == nil { - err = result.WaitForCompletionRef(context.TODO(), azcli.PollClient()) - } - require.Nil(t, err) -} diff --git a/builder/azure/chroot/diskset.go b/builder/azure/chroot/diskset.go deleted file mode 100644 index 0d7adb22f..000000000 --- a/builder/azure/chroot/diskset.go +++ /dev/null @@ -1,23 +0,0 @@ -package chroot - -import "github.com/hashicorp/packer/builder/azure/common/client" - -// Diskset represents all of the disks or snapshots associated with an image. -// It maps lun to resource ids. The OS disk is stored with lun=-1. -type Diskset map[int32]client.Resource - -// OS return the OS disk resource ID or nil if it is not assigned -func (ds Diskset) OS() *client.Resource { - if r, ok := ds[-1]; ok { - return &r - } - return nil -} - -// Data return the data disk resource ID or nil if it is not assigned -func (ds Diskset) Data(lun int32) *client.Resource { - if r, ok := ds[lun]; ok { - return &r - } - return nil -} diff --git a/builder/azure/chroot/diskset_test.go b/builder/azure/chroot/diskset_test.go deleted file mode 100644 index 7adc170ab..000000000 --- a/builder/azure/chroot/diskset_test.go +++ /dev/null @@ -1,16 +0,0 @@ -package chroot - -import "github.com/hashicorp/packer/builder/azure/common/client" - -// diskset easily creates a diskset for testing -func diskset(ids ...string) Diskset { - diskset := make(Diskset) - for i, id := range ids { - r, err := client.ParseResourceID(id) - if err != nil { - panic(err) - } - diskset[int32(i-1)] = r - } - return diskset -} diff --git a/builder/azure/chroot/metadatastub_test.go b/builder/azure/chroot/metadatastub_test.go deleted file mode 100644 index 575fcdafc..000000000 --- a/builder/azure/chroot/metadatastub_test.go +++ /dev/null @@ -1,18 +0,0 @@ -package chroot - -import "github.com/hashicorp/packer/builder/azure/common/client" - -func withMetadataStub(f func()) { - mdc := client.DefaultMetadataClient - defer func() { client.DefaultMetadataClient = mdc }() - client.DefaultMetadataClient = client.MetadataClientStub{ - ComputeInfo: client.ComputeInfo{ - SubscriptionID: "testSubscriptionID", - ResourceGroupName: "testResourceGroup", - Name: "testVM", - Location: "testLocation", - }, - } - - f() -} diff --git a/builder/azure/chroot/packerui_test.go b/builder/azure/chroot/packerui_test.go deleted file mode 100644 index 250d7063c..000000000 --- a/builder/azure/chroot/packerui_test.go +++ /dev/null @@ -1,19 +0,0 @@ -package chroot - -import ( - "io/ioutil" - "strings" - - packersdk "github.com/hashicorp/packer-plugin-sdk/packer" -) - -// testUI returns a test ui plus a function to retrieve the errors written to the ui -func testUI() (packersdk.Ui, func() string) { - errorBuffer := &strings.Builder{} - ui := &packersdk.BasicUi{ - Reader: strings.NewReader(""), - Writer: ioutil.Discard, - ErrorWriter: errorBuffer, - } - return ui, errorBuffer.String -} diff --git a/builder/azure/chroot/shared_image_gallery_destination.go b/builder/azure/chroot/shared_image_gallery_destination.go deleted file mode 100644 index de124f356..000000000 --- a/builder/azure/chroot/shared_image_gallery_destination.go +++ /dev/null @@ -1,71 +0,0 @@ -//go:generate packer-sdc struct-markdown -//go:generate packer-sdc mapstructure-to-hcl2 -type SharedImageGalleryDestination,TargetRegion - -package chroot - -import ( - "fmt" - "regexp" -) - -// SharedImageGalleryDestination models an image version in a Shared -// Image Gallery that can be used as a destination. -type SharedImageGalleryDestination struct { - ResourceGroup string `mapstructure:"resource_group" required:"true"` - GalleryName string `mapstructure:"gallery_name" required:"true"` - ImageName string `mapstructure:"image_name" required:"true"` - ImageVersion string `mapstructure:"image_version" required:"true"` - - TargetRegions []TargetRegion `mapstructure:"target_regions"` - ExcludeFromLatest bool `mapstructure:"exclude_from_latest"` - ExcludeFromLatestTypo bool `mapstructure:"exlude_from_latest" undocumented:"true"` -} - -// TargetRegion describes a region where the shared image should be replicated -type TargetRegion struct { - // Name of the Azure region - Name string `mapstructure:"name" required:"true"` - // Number of replicas in this region. Default: 1 - ReplicaCount int32 `mapstructure:"replicas"` - // Storage account type: Standard_LRS or Standard_ZRS. Default: Standard_ZRS - StorageAccountType string `mapstructure:"storage_account_type"` -} - -// ResourceID returns the resource ID string -func (sigd SharedImageGalleryDestination) ResourceID(subscriptionID string) string { - return fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Compute/galleries/%s/images/%s/versions/%s", - subscriptionID, - sigd.ResourceGroup, - sigd.GalleryName, - sigd.ImageName, - sigd.ImageVersion) -} - -// Validate validates that the values in the shared image are valid (without checking them on the network) -func (sigd *SharedImageGalleryDestination) Validate(prefix string) (errs []error, warns []string) { - if sigd.ResourceGroup == "" { - errs = append(errs, fmt.Errorf("%s.resource_group is required", prefix)) - } - if sigd.GalleryName == "" { - errs = append(errs, fmt.Errorf("%s.gallery_name is required", prefix)) - } - if sigd.ImageName == "" { - errs = append(errs, fmt.Errorf("%s.image_name is required", prefix)) - } - if match, err := regexp.MatchString("^[0-9]+\\.[0-9]+\\.[0-9]+$", sigd.ImageVersion); !match { - if err != nil { - warns = append(warns, fmt.Sprintf("Error matching pattern for %s.image_version: %s (this is probably a bug)", prefix, err)) - } - errs = append(errs, fmt.Errorf("%s.image_version should match '^[0-9]+\\.[0-9]+\\.[0-9]+$'", prefix)) - } - if len(sigd.TargetRegions) == 0 { - warns = append(warns, - fmt.Sprintf("%s.target_regions is empty; image will only be available in the region of the gallery", prefix)) - } - if sigd.ExcludeFromLatestTypo == true && sigd.ExcludeFromLatest == false { - warns = append(warns, - fmt.Sprintf("%s.exlude_from_latest is being deprecated, please use exclude_from_latest", prefix)) - sigd.ExcludeFromLatest = sigd.ExcludeFromLatestTypo - } - return -} diff --git a/builder/azure/chroot/shared_image_gallery_destination.hcl2spec.go b/builder/azure/chroot/shared_image_gallery_destination.hcl2spec.go deleted file mode 100644 index 3da1bc72d..000000000 --- a/builder/azure/chroot/shared_image_gallery_destination.hcl2spec.go +++ /dev/null @@ -1,70 +0,0 @@ -// Code generated by "packer-sdc mapstructure-to-hcl2"; DO NOT EDIT. - -package chroot - -import ( - "github.com/hashicorp/hcl/v2/hcldec" - "github.com/zclconf/go-cty/cty" -) - -// FlatSharedImageGalleryDestination is an auto-generated flat version of SharedImageGalleryDestination. -// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. -type FlatSharedImageGalleryDestination struct { - ResourceGroup *string `mapstructure:"resource_group" required:"true" cty:"resource_group" hcl:"resource_group"` - GalleryName *string `mapstructure:"gallery_name" required:"true" cty:"gallery_name" hcl:"gallery_name"` - ImageName *string `mapstructure:"image_name" required:"true" cty:"image_name" hcl:"image_name"` - ImageVersion *string `mapstructure:"image_version" required:"true" cty:"image_version" hcl:"image_version"` - TargetRegions []FlatTargetRegion `mapstructure:"target_regions" cty:"target_regions" hcl:"target_regions"` - ExcludeFromLatest *bool `mapstructure:"exclude_from_latest" cty:"exclude_from_latest" hcl:"exclude_from_latest"` - ExcludeFromLatestTypo *bool `mapstructure:"exlude_from_latest" undocumented:"true" cty:"exlude_from_latest" hcl:"exlude_from_latest"` -} - -// FlatMapstructure returns a new FlatSharedImageGalleryDestination. -// FlatSharedImageGalleryDestination is an auto-generated flat version of SharedImageGalleryDestination. -// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*SharedImageGalleryDestination) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { - return new(FlatSharedImageGalleryDestination) -} - -// HCL2Spec returns the hcl spec of a SharedImageGalleryDestination. -// This spec is used by HCL to read the fields of SharedImageGalleryDestination. -// The decoded values from this spec will then be applied to a FlatSharedImageGalleryDestination. -func (*FlatSharedImageGalleryDestination) HCL2Spec() map[string]hcldec.Spec { - s := map[string]hcldec.Spec{ - "resource_group": &hcldec.AttrSpec{Name: "resource_group", Type: cty.String, Required: false}, - "gallery_name": &hcldec.AttrSpec{Name: "gallery_name", Type: cty.String, Required: false}, - "image_name": &hcldec.AttrSpec{Name: "image_name", Type: cty.String, Required: false}, - "image_version": &hcldec.AttrSpec{Name: "image_version", Type: cty.String, Required: false}, - "target_regions": &hcldec.BlockListSpec{TypeName: "target_regions", Nested: hcldec.ObjectSpec((*FlatTargetRegion)(nil).HCL2Spec())}, - "exclude_from_latest": &hcldec.AttrSpec{Name: "exclude_from_latest", Type: cty.Bool, Required: false}, - "exlude_from_latest": &hcldec.AttrSpec{Name: "exlude_from_latest", Type: cty.Bool, Required: false}, - } - return s -} - -// FlatTargetRegion is an auto-generated flat version of TargetRegion. -// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. -type FlatTargetRegion struct { - Name *string `mapstructure:"name" required:"true" cty:"name" hcl:"name"` - ReplicaCount *int32 `mapstructure:"replicas" cty:"replicas" hcl:"replicas"` - StorageAccountType *string `mapstructure:"storage_account_type" cty:"storage_account_type" hcl:"storage_account_type"` -} - -// FlatMapstructure returns a new FlatTargetRegion. -// FlatTargetRegion is an auto-generated flat version of TargetRegion. -// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*TargetRegion) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { - return new(FlatTargetRegion) -} - -// HCL2Spec returns the hcl spec of a TargetRegion. -// This spec is used by HCL to read the fields of TargetRegion. -// The decoded values from this spec will then be applied to a FlatTargetRegion. -func (*FlatTargetRegion) HCL2Spec() map[string]hcldec.Spec { - s := map[string]hcldec.Spec{ - "name": &hcldec.AttrSpec{Name: "name", Type: cty.String, Required: false}, - "replicas": &hcldec.AttrSpec{Name: "replicas", Type: cty.Number, Required: false}, - "storage_account_type": &hcldec.AttrSpec{Name: "storage_account_type", Type: cty.String, Required: false}, - } - return s -} diff --git a/builder/azure/chroot/shared_image_gallery_destination_test.go b/builder/azure/chroot/shared_image_gallery_destination_test.go deleted file mode 100644 index 480c8ac7b..000000000 --- a/builder/azure/chroot/shared_image_gallery_destination_test.go +++ /dev/null @@ -1,158 +0,0 @@ -package chroot - -import ( - "reflect" - "strings" - "testing" -) - -func TestSharedImageGalleryDestination_ResourceID(t *testing.T) { - sigd := SharedImageGalleryDestination{ - ResourceGroup: "ResourceGroup", - GalleryName: "GalleryName", - ImageName: "ImageName", - ImageVersion: "ImageVersion", - } - want := "/subscriptions/SubscriptionID/resourceGroups/ResourceGroup/providers/Microsoft.Compute/galleries/GalleryName/images/ImageName/versions/ImageVersion" - if got := sigd.ResourceID("SubscriptionID"); !strings.EqualFold(got, want) { - t.Errorf("SharedImageGalleryDestination.ResourceID() = %v, want %v", got, want) - } -} - -func TestSharedImageGalleryDestination_Validate(t *testing.T) { - type fields struct { - ResourceGroup string - GalleryName string - ImageName string - ImageVersion string - TargetRegions []TargetRegion - ExcludeFromLatest bool - ExcludeFromLatestTypo bool - } - tests := []struct { - name string - fields fields - wantErrs []string - wantWarns []string - }{ - { - name: "complete", - fields: fields{ - ResourceGroup: "ResourceGroup", - GalleryName: "GalleryName", - ImageName: "ImageName", - ImageVersion: "0.1.2", - TargetRegions: []TargetRegion{ - TargetRegion{ - Name: "region1", - ReplicaCount: 5, - StorageAccountType: "Standard_ZRS", - }, - TargetRegion{ - Name: "region2", - ReplicaCount: 3, - StorageAccountType: "Standard_LRS", - }, - }, - ExcludeFromLatest: true, - }, - }, - { - name: "warn if target regions not specified", - fields: fields{ - ResourceGroup: "ResourceGroup", - GalleryName: "GalleryName", - ImageName: "ImageName", - ImageVersion: "0.1.2", - }, - wantWarns: []string{"sigdest.target_regions is empty; image will only be available in the region of the gallery"}, - }, - { - name: "warn if using exlude_from_latest", - fields: fields{ - ResourceGroup: "ResourceGroup", - GalleryName: "GalleryName", - ImageName: "ImageName", - ImageVersion: "0.1.2", - TargetRegions: []TargetRegion{ - TargetRegion{ - Name: "region1", - ReplicaCount: 5, - StorageAccountType: "Standard_ZRS", - }, - TargetRegion{ - Name: "region2", - ReplicaCount: 3, - StorageAccountType: "Standard_LRS", - }, - }, - ExcludeFromLatestTypo: true, - }, - wantWarns: []string{"sigdest.exlude_from_latest is being deprecated, please use exclude_from_latest"}, - }, - { - name: "version format", - wantErrs: []string{ - "sigdest.image_version should match '^[0-9]+\\.[0-9]+\\.[0-9]+$'", - }, - fields: fields{ - ResourceGroup: "ResourceGroup", - GalleryName: "GalleryName", - ImageName: "ImageName", - ImageVersion: "0.1.2alpha", - TargetRegions: []TargetRegion{ - TargetRegion{ - Name: "region1", - ReplicaCount: 5, - StorageAccountType: "Standard_ZRS", - }, - TargetRegion{ - Name: "region2", - ReplicaCount: 3, - StorageAccountType: "Standard_LRS", - }, - }, - ExcludeFromLatest: true, - }, - }, - { - name: "required fields", - wantErrs: []string{ - "sigdest.resource_group is required", - "sigdest.gallery_name is required", - "sigdest.image_name is required", - "sigdest.image_version should match '^[0-9]+\\.[0-9]+\\.[0-9]+$'", - }, - wantWarns: []string{"sigdest.target_regions is empty; image will only be available in the region of the gallery"}, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - sigd := &SharedImageGalleryDestination{ - ResourceGroup: tt.fields.ResourceGroup, - GalleryName: tt.fields.GalleryName, - ImageName: tt.fields.ImageName, - ImageVersion: tt.fields.ImageVersion, - TargetRegions: tt.fields.TargetRegions, - ExcludeFromLatest: tt.fields.ExcludeFromLatest, - ExcludeFromLatestTypo: tt.fields.ExcludeFromLatestTypo, - } - gotErrs, gotWarns := sigd.Validate("sigdest") - - var gotStrErrs []string - if gotErrs != nil { - gotStrErrs = make([]string, len(gotErrs)) - for i, e := range gotErrs { - gotStrErrs[i] = e.Error() - } - } - - if !reflect.DeepEqual(gotStrErrs, tt.wantErrs) { - t.Errorf("SharedImageGalleryDestination.Validate() gotErrs = %q, want %q", gotStrErrs, tt.wantErrs) - } - if !reflect.DeepEqual(gotWarns, tt.wantWarns) { - t.Errorf("SharedImageGalleryDestination.Validate() gotWarns = %q, want %q", gotWarns, tt.wantWarns) - } - }) - } -} diff --git a/builder/azure/chroot/step_attach_disk.go b/builder/azure/chroot/step_attach_disk.go deleted file mode 100644 index 7d8c83425..000000000 --- a/builder/azure/chroot/step_attach_disk.go +++ /dev/null @@ -1,85 +0,0 @@ -package chroot - -import ( - "context" - "fmt" - "log" - "time" - - "github.com/hashicorp/packer-plugin-sdk/multistep" - packersdk "github.com/hashicorp/packer-plugin-sdk/packer" - "github.com/hashicorp/packer/builder/azure/common/client" -) - -var _ multistep.Step = &StepAttachDisk{} - -type StepAttachDisk struct { - attached bool -} - -func (s *StepAttachDisk) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { - azcli := state.Get("azureclient").(client.AzureClientSet) - ui := state.Get("ui").(packersdk.Ui) - diskset := state.Get(stateBagKey_Diskset).(Diskset) - diskResourceID := diskset.OS().String() - - ui.Say(fmt.Sprintf("Attaching disk '%s'", diskResourceID)) - - da := NewDiskAttacher(azcli) - lun, err := da.AttachDisk(ctx, diskResourceID) - if err != nil { - log.Printf("StepAttachDisk.Run: error: %+v", err) - err := fmt.Errorf( - "error attaching disk '%s': %v", diskResourceID, err) - state.Put("error", err) - ui.Error(err.Error()) - return multistep.ActionHalt - } - - ui.Say("Disk attached, waiting for device to show up") - ctx, cancel := context.WithTimeout(ctx, time.Minute*3) // in case is not configured correctly - defer cancel() - device, err := da.WaitForDevice(ctx, lun) - if err != nil { - log.Printf("StepAttachDisk.Run: error: %+v", err) - err := fmt.Errorf( - "error attaching disk '%s': %v", diskResourceID, err) - state.Put("error", err) - ui.Error(err.Error()) - return multistep.ActionHalt - } - - ui.Say(fmt.Sprintf("Disk available at %q", device)) - s.attached = true - state.Put("device", device) - state.Put("attach_cleanup", s) - return multistep.ActionContinue -} - -func (s *StepAttachDisk) Cleanup(state multistep.StateBag) { - ui := state.Get("ui").(packersdk.Ui) - if err := s.CleanupFunc(state); err != nil { - ui.Error(err.Error()) - } -} - -func (s *StepAttachDisk) CleanupFunc(state multistep.StateBag) error { - - if s.attached { - azcli := state.Get("azureclient").(client.AzureClientSet) - ui := state.Get("ui").(packersdk.Ui) - diskset := state.Get(stateBagKey_Diskset).(Diskset) - diskResourceID := diskset.OS().String() - - ui.Say(fmt.Sprintf("Detaching disk '%s'", diskResourceID)) - - da := NewDiskAttacher(azcli) - err := da.DetachDisk(context.Background(), diskResourceID) - if err != nil { - return fmt.Errorf("error detaching %q: %v", diskResourceID, err) - } - s.attached = false - } - - return nil -} diff --git a/builder/azure/chroot/step_attach_disk_test.go b/builder/azure/chroot/step_attach_disk_test.go deleted file mode 100644 index f512c015c..000000000 --- a/builder/azure/chroot/step_attach_disk_test.go +++ /dev/null @@ -1,131 +0,0 @@ -package chroot - -import ( - "context" - "errors" - "io/ioutil" - "net/http" - "reflect" - "strings" - "testing" - - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-12-01/compute" - "github.com/Azure/go-autorest/autorest" - "github.com/hashicorp/packer-plugin-sdk/multistep" - packersdk "github.com/hashicorp/packer-plugin-sdk/packer" - "github.com/hashicorp/packer/builder/azure/common/client" -) - -func TestStepAttachDisk_Run(t *testing.T) { - type fields struct { - GetDiskResponseCode int - GetDiskResponseBody string - - attachError error - waitForDeviceError error - } - tests := []struct { - name string - fields fields - want multistep.StepAction - }{ - { - name: "HappyPath", - want: multistep.ActionContinue, - }, - { - name: "AttachError", - fields: fields{ - attachError: errors.New("unit test"), - }, - want: multistep.ActionHalt, - }, - { - name: "WaitForDeviceError", - fields: fields{ - waitForDeviceError: errors.New("unit test"), - }, - want: multistep.ActionHalt, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - s := &StepAttachDisk{} - - NewDiskAttacher = func(azcli client.AzureClientSet) DiskAttacher { - return &fakeDiskAttacher{ - attachError: tt.fields.attachError, - waitForDeviceError: tt.fields.waitForDeviceError, - } - } - - dm := compute.NewDisksClient("subscriptionId") - dm.Sender = autorest.SenderFunc(func(r *http.Request) (*http.Response, error) { - return &http.Response{ - Request: r, - Body: ioutil.NopCloser(strings.NewReader(tt.fields.GetDiskResponseBody)), - StatusCode: tt.fields.GetDiskResponseCode, - }, nil - }) - - errorBuffer := &strings.Builder{} - ui := &packersdk.BasicUi{ - Reader: strings.NewReader(""), - Writer: ioutil.Discard, - ErrorWriter: errorBuffer, - } - - state := new(multistep.BasicStateBag) - state.Put("azureclient", &client.AzureClientSetMock{}) - state.Put("ui", ui) - state.Put(stateBagKey_Diskset, diskset("/subscriptions/12345/resourceGroups/group1/providers/Microsoft.Compute/disks/disk1")) - - got := s.Run(context.TODO(), state) - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("StepAttachDisk.Run() = %v, want %v", got, tt.want) - } - - if got == multistep.ActionHalt { - if _, ok := state.GetOk("error"); !ok { - t.Fatal("Expected 'error' to be set in statebag after failure") - } - } - }) - } -} - -type fakeDiskAttacher struct { - attachError error - waitForDeviceError error -} - -var _ DiskAttacher = &fakeDiskAttacher{} - -func (da *fakeDiskAttacher) AttachDisk(ctx context.Context, disk string) (lun int32, err error) { - if da.attachError != nil { - return 0, da.attachError - } - return 3, nil -} - -func (da *fakeDiskAttacher) DiskPathForLun(lun int32) string { - panic("not implemented") -} - -func (da *fakeDiskAttacher) WaitForDevice(ctx context.Context, lun int32) (device string, err error) { - if da.waitForDeviceError != nil { - return "", da.waitForDeviceError - } - if lun == 3 { - return "/dev/sdq", nil - } - panic("expected lun==3") -} - -func (da *fakeDiskAttacher) DetachDisk(ctx context.Context, disk string) (err error) { - panic("not implemented") -} - -func (da *fakeDiskAttacher) WaitForDetach(ctx context.Context, diskID string) error { - panic("not implemented") -} diff --git a/builder/azure/chroot/step_create_image.go b/builder/azure/chroot/step_create_image.go deleted file mode 100644 index 4c61a9b11..000000000 --- a/builder/azure/chroot/step_create_image.go +++ /dev/null @@ -1,112 +0,0 @@ -package chroot - -import ( - "context" - "fmt" - "log" - "sort" - - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-12-01/compute" - "github.com/Azure/go-autorest/autorest/azure" - "github.com/Azure/go-autorest/autorest/to" - "github.com/hashicorp/packer-plugin-sdk/multistep" - packersdk "github.com/hashicorp/packer-plugin-sdk/packer" - "github.com/hashicorp/packer/builder/azure/common/client" -) - -var _ multistep.Step = &StepCreateImage{} - -type StepCreateImage struct { - ImageResourceID string - ImageOSState string - OSDiskStorageAccountType string - OSDiskCacheType string - DataDiskStorageAccountType string - DataDiskCacheType string - Location string -} - -func (s *StepCreateImage) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { - azcli := state.Get("azureclient").(client.AzureClientSet) - ui := state.Get("ui").(packersdk.Ui) - diskset := state.Get(stateBagKey_Diskset).(Diskset) - diskResourceID := diskset.OS().String() - - ui.Say(fmt.Sprintf("Creating image %s\n using %s for os disk.", - s.ImageResourceID, - diskResourceID)) - - imageResource, err := azure.ParseResourceID(s.ImageResourceID) - - if err != nil { - log.Printf("StepCreateImage.Run: error: %+v", err) - err := fmt.Errorf( - "error parsing image resource id '%s': %v", s.ImageResourceID, err) - state.Put("error", err) - ui.Error(err.Error()) - return multistep.ActionHalt - } - - image := compute.Image{ - Location: to.StringPtr(s.Location), - ImageProperties: &compute.ImageProperties{ - StorageProfile: &compute.ImageStorageProfile{ - OsDisk: &compute.ImageOSDisk{ - OsState: compute.OperatingSystemStateTypes(s.ImageOSState), - OsType: compute.Linux, - ManagedDisk: &compute.SubResource{ - ID: &diskResourceID, - }, - StorageAccountType: compute.StorageAccountTypes(s.OSDiskStorageAccountType), - Caching: compute.CachingTypes(s.OSDiskCacheType), - }, - // DataDisks: nil, - // ZoneResilient: nil, - }, - }, - // Tags: nil, - } - - var datadisks []compute.ImageDataDisk - for lun, resource := range diskset { - if lun != -1 { - ui.Say(fmt.Sprintf(" using %q for data disk (lun %d).", resource, lun)) - - datadisks = append(datadisks, compute.ImageDataDisk{ - Lun: to.Int32Ptr(lun), - ManagedDisk: &compute.SubResource{ID: to.StringPtr(resource.String())}, - StorageAccountType: compute.StorageAccountTypes(s.DataDiskStorageAccountType), - Caching: compute.CachingTypes(s.DataDiskCacheType), - }) - } - } - if datadisks != nil { - sort.Slice(datadisks, func(i, j int) bool { - return *datadisks[i].Lun < *datadisks[j].Lun - }) - image.ImageProperties.StorageProfile.DataDisks = &datadisks - } - - f, err := azcli.ImagesClient().CreateOrUpdate( - ctx, - imageResource.ResourceGroup, - imageResource.ResourceName, - image) - if err == nil { - log.Println("Image creation in process...") - err = f.WaitForCompletionRef(ctx, azcli.PollClient()) - } - if err != nil { - log.Printf("StepCreateImage.Run: error: %+v", err) - err := fmt.Errorf( - "error creating image '%s': %v", s.ImageResourceID, err) - state.Put("error", err) - ui.Error(err.Error()) - return multistep.ActionHalt - } - log.Printf("Image creation complete: %s", f.Status()) - - return multistep.ActionContinue -} - -func (*StepCreateImage) Cleanup(bag multistep.StateBag) {} // this is the final artifact, don't delete diff --git a/builder/azure/chroot/step_create_image_test.go b/builder/azure/chroot/step_create_image_test.go deleted file mode 100644 index 054771715..000000000 --- a/builder/azure/chroot/step_create_image_test.go +++ /dev/null @@ -1,137 +0,0 @@ -package chroot - -import ( - "context" - "io/ioutil" - "net/http" - "reflect" - "regexp" - "testing" - - "github.com/hashicorp/packer-plugin-sdk/multistep" - packersdk "github.com/hashicorp/packer-plugin-sdk/packer" - "github.com/hashicorp/packer/builder/azure/common/client" - - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-12-01/compute" - "github.com/Azure/go-autorest/autorest" -) - -func TestStepCreateImage_Run(t *testing.T) { - type fields struct { - ImageResourceID string - ImageOSState string - OSDiskStorageAccountType string - OSDiskCacheType string - DataDiskStorageAccountType string - DataDiskCacheType string - Location string - } - tests := []struct { - name string - fields fields - diskset Diskset - want multistep.StepAction - wantPutBody string - }{ - { - name: "happy path", - fields: fields{ - ImageResourceID: "/subscriptions/12345/resourceGroups/group1/providers/Microsoft.Compute/images/myImage", - Location: "location1", - OSDiskStorageAccountType: "Standard_LRS", - OSDiskCacheType: "ReadWrite", - DataDiskStorageAccountType: "Premium_LRS", - DataDiskCacheType: "ReadOnly", - }, - diskset: diskset( - "/subscriptions/12345/resourceGroups/group1/providers/Microsoft.Compute/disks/osdisk", - "/subscriptions/12345/resourceGroups/group1/providers/Microsoft.Compute/disks/datadisk0", - "/subscriptions/12345/resourceGroups/group1/providers/Microsoft.Compute/disks/datadisk1", - "/subscriptions/12345/resourceGroups/group1/providers/Microsoft.Compute/disks/datadisk2"), - want: multistep.ActionContinue, - wantPutBody: `{ - "location": "location1", - "properties": { - "storageProfile": { - "osDisk": { - "osType": "Linux", - "managedDisk": { - "id": "/subscriptions/12345/resourceGroups/group1/providers/Microsoft.Compute/disks/osdisk" - }, - "caching": "ReadWrite", - "storageAccountType": "Standard_LRS" - }, - "dataDisks": [ - { - "lun": 0, - "managedDisk": { - "id": "/subscriptions/12345/resourceGroups/group1/providers/Microsoft.Compute/disks/datadisk0" - }, - "caching": "ReadOnly", - "storageAccountType": "Premium_LRS" - }, - { - "lun": 1, - "managedDisk": { - "id": "/subscriptions/12345/resourceGroups/group1/providers/Microsoft.Compute/disks/datadisk1" - }, - "caching": "ReadOnly", - "storageAccountType": "Premium_LRS" - }, - { - "lun": 2, - "managedDisk": { - "id": "/subscriptions/12345/resourceGroups/group1/providers/Microsoft.Compute/disks/datadisk2" - }, - "caching": "ReadOnly", - "storageAccountType": "Premium_LRS" - } - ] - } - } - }`, - }, - } - for _, tt := range tests { - - ic := compute.NewImagesClient("subscriptionID") - ic.Sender = autorest.SenderFunc(func(r *http.Request) (*http.Response, error) { - if r.Method != "PUT" { - t.Fatal("Expected only a PUT call") - } - if tt.wantPutBody != "" { - b, _ := ioutil.ReadAll(r.Body) - expectedPutBody := regexp.MustCompile(`[\s\n]`).ReplaceAllString(tt.wantPutBody, "") - if string(b) != expectedPutBody { - t.Errorf("expected body to be %v, but got %v", expectedPutBody, string(b)) - } - } - return &http.Response{ - Request: r, - StatusCode: 200, - }, nil - }) - - state := new(multistep.BasicStateBag) - state.Put("azureclient", &client.AzureClientSetMock{ - ImagesClientMock: ic, - }) - state.Put("ui", packersdk.TestUi(t)) - state.Put(stateBagKey_Diskset, tt.diskset) - - t.Run(tt.name, func(t *testing.T) { - s := &StepCreateImage{ - ImageResourceID: tt.fields.ImageResourceID, - ImageOSState: tt.fields.ImageOSState, - OSDiskStorageAccountType: tt.fields.OSDiskStorageAccountType, - OSDiskCacheType: tt.fields.OSDiskCacheType, - DataDiskStorageAccountType: tt.fields.DataDiskStorageAccountType, - DataDiskCacheType: tt.fields.DataDiskCacheType, - Location: tt.fields.Location, - } - if got := s.Run(context.TODO(), state); !reflect.DeepEqual(got, tt.want) { - t.Errorf("StepCreateImage.Run() = %v, want %v", got, tt.want) - } - }) - } -} diff --git a/builder/azure/chroot/step_create_new_diskset.go b/builder/azure/chroot/step_create_new_diskset.go deleted file mode 100644 index 1eb1612b1..000000000 --- a/builder/azure/chroot/step_create_new_diskset.go +++ /dev/null @@ -1,239 +0,0 @@ -package chroot - -import ( - "context" - "fmt" - "log" - "strings" - "time" - - "github.com/hashicorp/packer-plugin-sdk/multistep" - packersdk "github.com/hashicorp/packer-plugin-sdk/packer" - "github.com/hashicorp/packer/builder/azure/common/client" - - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-12-01/compute" - "github.com/Azure/go-autorest/autorest/to" -) - -var _ multistep.Step = &StepCreateNewDiskset{} - -type StepCreateNewDiskset struct { - OSDiskID string // Disk ID - OSDiskSizeGB int32 // optional, ignored if 0 - OSDiskStorageAccountType string // from compute.DiskStorageAccountTypes - DataDiskStorageAccountType string // from compute.DiskStorageAccountTypes - - DataDiskIDPrefix string - - disks Diskset - - HyperVGeneration string // For OS disk - - // Copy another disk - SourceOSDiskResourceID string - - // Extract from platform image - SourcePlatformImage *client.PlatformImage - // Extract from shared image - SourceImageResourceID string - // Location is needed for platform and shared images - Location string - - SkipCleanup bool -} - -func (s *StepCreateNewDiskset) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { - azcli := state.Get("azureclient").(client.AzureClientSet) - ui := state.Get("ui").(packersdk.Ui) - - s.disks = make(Diskset) - - errorMessage := func(format string, params ...interface{}) multistep.StepAction { - err := fmt.Errorf("StepCreateNewDiskset.Run: error: "+format, params...) - state.Put("error", err) - ui.Error(err.Error()) - return multistep.ActionHalt - } - - // we always have an OS disk - osDisk, err := client.ParseResourceID(s.OSDiskID) - if err != nil { - return errorMessage("error parsing resource id '%s': %v", s.OSDiskID, err) - } - if !strings.EqualFold(osDisk.Provider, "Microsoft.Compute") || - !strings.EqualFold(osDisk.ResourceType.String(), "disks") { - return errorMessage("Resource %q is not of type Microsoft.Compute/disks", s.OSDiskID) - } - - // transform step config to disk model - disk := s.getOSDiskDefinition(azcli.SubscriptionID()) - - // Initiate disk creation - f, err := azcli.DisksClient().CreateOrUpdate(ctx, osDisk.ResourceGroup, osDisk.ResourceName.String(), disk) - if err != nil { - return errorMessage("Failed to initiate resource creation: %q", osDisk) - } - s.disks[-1] = osDisk // save the resoure we just create in our disk set - state.Put(stateBagKey_Diskset, s.disks) // update the statebag - ui.Say(fmt.Sprintf("Creating disk %q", s.OSDiskID)) - - type Future struct { - client.Resource - compute.DisksCreateOrUpdateFuture - } - futures := []Future{{osDisk, f}} - - if s.SourceImageResourceID != "" { - // retrieve image to see if there are any datadisks - imageID, err := client.ParseResourceID(s.SourceImageResourceID) - if err != nil { - return errorMessage("could not parse source image id %q: %v", s.SourceImageResourceID, err) - } - if !strings.EqualFold(imageID.Provider+"/"+imageID.ResourceType.String(), - "Microsoft.Compute/galleries/images/versions") { - return errorMessage("source image id is not a shared image version %q, expected type 'Microsoft.Compute/galleries/images/versions'", imageID) - } - image, err := azcli.GalleryImageVersionsClient().Get(ctx, - imageID.ResourceGroup, - imageID.ResourceName[0], imageID.ResourceName[1], imageID.ResourceName[2], "") - if err != nil { - return errorMessage("error retrieving source image %q: %v", imageID, err) - } - if image.GalleryImageVersionProperties != nil && - image.GalleryImageVersionProperties.StorageProfile != nil && - image.GalleryImageVersionProperties.StorageProfile.DataDiskImages != nil { - for i, ddi := range *image.GalleryImageVersionProperties.StorageProfile.DataDiskImages { - if ddi.Lun == nil { - return errorMessage("unexpected: lun is null for data disk # %d", i) - } - datadiskID, err := client.ParseResourceID(fmt.Sprintf("%s%d", s.DataDiskIDPrefix, *ddi.Lun)) - if err != nil { - return errorMessage("unable to construct resource id for datadisk: %v", err) - } - - disk := s.getDatadiskDefinitionFromImage(*ddi.Lun) - // Initiate disk creation - f, err := azcli.DisksClient().CreateOrUpdate(ctx, datadiskID.ResourceGroup, datadiskID.ResourceName.String(), disk) - if err != nil { - return errorMessage("Failed to initiate resource creation: %q", datadiskID) - } - s.disks[*ddi.Lun] = datadiskID // save the resoure we just create in our disk set - state.Put(stateBagKey_Diskset, s.disks) // update the statebag - ui.Say(fmt.Sprintf("Creating disk %q", datadiskID)) - - futures = append(futures, Future{datadiskID, f}) - } - } - } - - ui.Say("Waiting for disks to be created.") - - // Wait for completion - for _, f := range futures { - cli := azcli.PollClient() // quick polling for quick operations - cli.PollingDelay = time.Second - err = f.WaitForCompletionRef(ctx, cli) - if err != nil { - return errorMessage( - "error creating new disk '%s': %v", f.Resource, err) - } - ui.Say(fmt.Sprintf("Disk %q created", f.Resource)) - } - - return multistep.ActionContinue -} - -func (s StepCreateNewDiskset) getOSDiskDefinition(subscriptionID string) compute.Disk { - disk := compute.Disk{ - Location: to.StringPtr(s.Location), - DiskProperties: &compute.DiskProperties{ - OsType: "Linux", - CreationData: &compute.CreationData{}, - }, - } - - if s.OSDiskStorageAccountType != "" { - disk.Sku = &compute.DiskSku{ - Name: compute.DiskStorageAccountTypes(s.OSDiskStorageAccountType), - } - } - - if s.HyperVGeneration != "" { - disk.DiskProperties.HyperVGeneration = compute.HyperVGeneration(s.HyperVGeneration) - } - - if s.OSDiskSizeGB > 0 { - disk.DiskProperties.DiskSizeGB = to.Int32Ptr(s.OSDiskSizeGB) - } - - switch { - case s.SourcePlatformImage != nil: - disk.CreationData.CreateOption = compute.FromImage - disk.CreationData.ImageReference = &compute.ImageDiskReference{ - ID: to.StringPtr(fmt.Sprintf( - "/subscriptions/%s/providers/Microsoft.Compute/locations/%s/publishers/%s/artifacttypes/vmimage/offers/%s/skus/%s/versions/%s", - subscriptionID, s.Location, - s.SourcePlatformImage.Publisher, s.SourcePlatformImage.Offer, s.SourcePlatformImage.Sku, s.SourcePlatformImage.Version)), - } - case s.SourceOSDiskResourceID != "": - disk.CreationData.CreateOption = compute.Copy - disk.CreationData.SourceResourceID = to.StringPtr(s.SourceOSDiskResourceID) - case s.SourceImageResourceID != "": - disk.CreationData.CreateOption = compute.FromImage - disk.CreationData.GalleryImageReference = &compute.ImageDiskReference{ - ID: to.StringPtr(s.SourceImageResourceID), - } - default: - disk.CreationData.CreateOption = compute.Empty - } - return disk -} - -func (s StepCreateNewDiskset) getDatadiskDefinitionFromImage(lun int32) compute.Disk { - disk := compute.Disk{ - Location: to.StringPtr(s.Location), - DiskProperties: &compute.DiskProperties{ - CreationData: &compute.CreationData{}, - }, - } - - disk.CreationData.CreateOption = compute.FromImage - disk.CreationData.GalleryImageReference = &compute.ImageDiskReference{ - ID: to.StringPtr(s.SourceImageResourceID), - Lun: to.Int32Ptr(lun), - } - - if s.DataDiskStorageAccountType != "" { - disk.Sku = &compute.DiskSku{ - Name: compute.DiskStorageAccountTypes(s.DataDiskStorageAccountType), - } - } - return disk -} - -func (s *StepCreateNewDiskset) Cleanup(state multistep.StateBag) { - if !s.SkipCleanup { - azcli := state.Get("azureclient").(client.AzureClientSet) - ui := state.Get("ui").(packersdk.Ui) - - for _, d := range s.disks { - - ui.Say(fmt.Sprintf("Waiting for disk %q detach to complete", d)) - err := NewDiskAttacher(azcli).WaitForDetach(context.Background(), d.String()) - if err != nil { - ui.Error(fmt.Sprintf("error detaching disk %q: %s", d, err)) - } - - ui.Say(fmt.Sprintf("Deleting disk %q", d)) - - f, err := azcli.DisksClient().Delete(context.TODO(), d.ResourceGroup, d.ResourceName.String()) - if err == nil { - err = f.WaitForCompletionRef(context.TODO(), azcli.PollClient()) - } - if err != nil { - log.Printf("StepCreateNewDiskset.Cleanup: error: %+v", err) - ui.Error(fmt.Sprintf("error deleting disk '%s': %v.", d, err)) - } - } - } -} diff --git a/builder/azure/chroot/step_create_new_diskset_test.go b/builder/azure/chroot/step_create_new_diskset_test.go deleted file mode 100644 index 470c25dec..000000000 --- a/builder/azure/chroot/step_create_new_diskset_test.go +++ /dev/null @@ -1,247 +0,0 @@ -package chroot - -import ( - "context" - "io/ioutil" - "net/http" - "reflect" - "regexp" - "strings" - "testing" - - "github.com/hashicorp/packer-plugin-sdk/multistep" - packersdk "github.com/hashicorp/packer-plugin-sdk/packer" - "github.com/hashicorp/packer/builder/azure/common/client" - - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-12-01/compute" - "github.com/Azure/go-autorest/autorest" -) - -func TestStepCreateNewDisk_Run(t *testing.T) { - tests := []struct { - name string - fields StepCreateNewDiskset - expectedPutDiskBodies []string - want multistep.StepAction - verifyDiskset *Diskset - }{ - { - name: "from disk", - fields: StepCreateNewDiskset{ - OSDiskID: "/subscriptions/SubscriptionID/resourcegroups/ResourceGroupName/providers/Microsoft.Compute/disks/TemporaryOSDiskName", - OSDiskSizeGB: 42, - OSDiskStorageAccountType: string(compute.PremiumLRS), - HyperVGeneration: string(compute.V1), - Location: "westus", - SourceOSDiskResourceID: "SourceDisk", - }, - expectedPutDiskBodies: []string{` - { - "location": "westus", - "properties": { - "osType": "Linux", - "hyperVGeneration": "V1", - "creationData": { - "createOption": "Copy", - "sourceResourceId": "SourceDisk" - }, - "diskSizeGB": 42 - }, - "sku": { - "name": "Premium_LRS" - } - }`}, - want: multistep.ActionContinue, - verifyDiskset: &Diskset{-1: resource("/subscriptions/SubscriptionID/resourceGroups/ResourceGroupName/providers/Microsoft.Compute/disks/TemporaryOSDiskName")}, - }, - { - name: "from platform image", - fields: StepCreateNewDiskset{ - OSDiskID: "/subscriptions/SubscriptionID/resourcegroups/ResourceGroupName/providers/Microsoft.Compute/disks/TemporaryOSDiskName", - OSDiskStorageAccountType: string(compute.StandardLRS), - HyperVGeneration: string(compute.V1), - Location: "westus", - SourcePlatformImage: &client.PlatformImage{ - Publisher: "Microsoft", - Offer: "Windows", - Sku: "2016-DataCenter", - Version: "2016.1.4", - }, - }, - expectedPutDiskBodies: []string{` - { - "location": "westus", - "properties": { - "osType": "Linux", - "hyperVGeneration": "V1", - "creationData": { - "createOption":"FromImage", - "imageReference": { - "id":"/subscriptions/SubscriptionID/providers/Microsoft.Compute/locations/westus/publishers/Microsoft/artifacttypes/vmimage/offers/Windows/skus/2016-DataCenter/versions/2016.1.4" - } - } - }, - "sku": { - "name": "Standard_LRS" - } - }`}, - want: multistep.ActionContinue, - verifyDiskset: &Diskset{-1: resource("/subscriptions/SubscriptionID/resourceGroups/ResourceGroupName/providers/Microsoft.Compute/disks/TemporaryOSDiskName")}, - }, - { - name: "from shared image", - fields: StepCreateNewDiskset{ - OSDiskID: "/subscriptions/SubscriptionID/resourcegroups/ResourceGroupName/providers/Microsoft.Compute/disks/TemporaryOSDiskName", - OSDiskStorageAccountType: string(compute.StandardLRS), - DataDiskStorageAccountType: string(compute.PremiumLRS), - DataDiskIDPrefix: "/subscriptions/SubscriptionID/resourcegroups/ResourceGroupName/providers/Microsoft.Compute/disks/TemporaryDataDisk-", - HyperVGeneration: string(compute.V1), - Location: "westus", - SourceImageResourceID: "/subscriptions/SubscriptionID/resourcegroups/imagegroup/providers/Microsoft.Compute/galleries/MyGallery/images/MyImage/versions/1.2.3", - }, - - expectedPutDiskBodies: []string{` - { - "location": "westus", - "properties": { - "osType": "Linux", - "hyperVGeneration": "V1", - "creationData": { - "createOption":"FromImage", - "galleryImageReference": { - "id":"/subscriptions/SubscriptionID/resourcegroups/imagegroup/providers/Microsoft.Compute/galleries/MyGallery/images/MyImage/versions/1.2.3" - } - } - }, - "sku": { - "name": "Standard_LRS" - } - }`, ` - { - "location": "westus", - "properties": { - "creationData": { - "createOption":"FromImage", - "galleryImageReference": { - "id": "/subscriptions/SubscriptionID/resourcegroups/imagegroup/providers/Microsoft.Compute/galleries/MyGallery/images/MyImage/versions/1.2.3", - "lun": 5 - } - } - }, - "sku": { - "name": "Premium_LRS" - } - }`, ` - { - "location": "westus", - "properties": { - "creationData": { - "createOption":"FromImage", - "galleryImageReference": { - "id": "/subscriptions/SubscriptionID/resourcegroups/imagegroup/providers/Microsoft.Compute/galleries/MyGallery/images/MyImage/versions/1.2.3", - "lun": 9 - } - } - }, - "sku": { - "name": "Premium_LRS" - } - }`, ` - { - "location": "westus", - "properties": { - "creationData": { - "createOption":"FromImage", - "galleryImageReference": { - "id": "/subscriptions/SubscriptionID/resourcegroups/imagegroup/providers/Microsoft.Compute/galleries/MyGallery/images/MyImage/versions/1.2.3", - "lun": 3 - } - } - }, - "sku": { - "name": "Premium_LRS" - } - }`}, - want: multistep.ActionContinue, - verifyDiskset: &Diskset{ - -1: resource("/subscriptions/SubscriptionID/resourceGroups/ResourceGroupName/providers/Microsoft.Compute/disks/TemporaryOSDiskName"), - 3: resource("/subscriptions/SubscriptionID/resourceGroups/ResourceGroupName/providers/Microsoft.Compute/disks/TemporaryDataDisk-3"), - 5: resource("/subscriptions/SubscriptionID/resourceGroups/ResourceGroupName/providers/Microsoft.Compute/disks/TemporaryDataDisk-5"), - 9: resource("/subscriptions/SubscriptionID/resourceGroups/ResourceGroupName/providers/Microsoft.Compute/disks/TemporaryDataDisk-9"), - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - s := tt.fields - - bodyCount := 0 - m := compute.NewDisksClient("SubscriptionID") - m.Sender = autorest.SenderFunc(func(r *http.Request) (*http.Response, error) { - if r.Method != "PUT" { - t.Fatal("Expected only a PUT disk call") - } - b, _ := ioutil.ReadAll(r.Body) - expectedPutDiskBody := regexp.MustCompile(`[\s\n]`).ReplaceAllString(tt.expectedPutDiskBodies[bodyCount], "") - bodyCount++ - if string(b) != expectedPutDiskBody { - t.Fatalf("expected body #%d to be %q, but got %q", bodyCount, expectedPutDiskBody, string(b)) - } - return &http.Response{ - Request: r, - StatusCode: 200, - }, nil - }) - - giv := compute.NewGalleryImageVersionsClient("SubscriptionID") - giv.Sender = autorest.SenderFunc(func(r *http.Request) (*http.Response, error) { - if r.Method == "GET" && - regexp.MustCompile(`(?i)/versions/1\.2\.3$`).MatchString(r.URL.Path) { - return &http.Response{ - Request: r, - Body: ioutil.NopCloser(strings.NewReader(`{ - "properties": { "storageProfile": { - "dataDiskImages":[ - { "lun": 5 }, - { "lun": 9 }, - { "lun": 3 } - ] - } } - }`)), - StatusCode: 200, - }, nil - } - return &http.Response{ - Request: r, - Status: "Unexpected request", - StatusCode: 500, - }, nil - }) - - state := new(multistep.BasicStateBag) - state.Put("azureclient", &client.AzureClientSetMock{ - SubscriptionIDMock: "SubscriptionID", - DisksClientMock: m, - GalleryImageVersionsClientMock: giv, - }) - state.Put("ui", packersdk.TestUi(t)) - - if got := s.Run(context.TODO(), state); !reflect.DeepEqual(got, tt.want) { - t.Errorf("StepCreateNewDisk.Run() = %v, want %v", got, tt.want) - } - - ds := state.Get(stateBagKey_Diskset) - if tt.verifyDiskset != nil && !reflect.DeepEqual(*tt.verifyDiskset, ds) { - t.Errorf("Error verifying diskset after Run(), got %v, want %v", ds, *tt.verifyDiskset) - } - }) - } -} - -func resource(id string) client.Resource { - v, err := client.ParseResourceID(id) - if err != nil { - panic(err) - } - return v -} diff --git a/builder/azure/chroot/step_create_shared_image_version.go b/builder/azure/chroot/step_create_shared_image_version.go deleted file mode 100644 index 4e4843442..000000000 --- a/builder/azure/chroot/step_create_shared_image_version.go +++ /dev/null @@ -1,108 +0,0 @@ -package chroot - -import ( - "context" - "fmt" - "log" - "sort" - "time" - - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-12-01/compute" - "github.com/Azure/go-autorest/autorest/to" - "github.com/hashicorp/packer-plugin-sdk/multistep" - packersdk "github.com/hashicorp/packer-plugin-sdk/packer" - "github.com/hashicorp/packer/builder/azure/common/client" -) - -type StepCreateSharedImageVersion struct { - Destination SharedImageGalleryDestination - OSDiskCacheType string - DataDiskCacheType string - Location string -} - -func (s *StepCreateSharedImageVersion) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { - azcli := state.Get("azureclient").(client.AzureClientSet) - ui := state.Get("ui").(packersdk.Ui) - snapshotset := state.Get(stateBagKey_Snapshotset).(Diskset) - - ui.Say(fmt.Sprintf("Creating image version %s\n using %q for os disk.", - s.Destination.ResourceID(azcli.SubscriptionID()), - snapshotset.OS())) - - var targetRegions []compute.TargetRegion - // transform target regions to API objects - for _, tr := range s.Destination.TargetRegions { - apiObject := compute.TargetRegion{ - Name: to.StringPtr(tr.Name), - RegionalReplicaCount: to.Int32Ptr(tr.ReplicaCount), - StorageAccountType: compute.StorageAccountType(tr.StorageAccountType), - } - targetRegions = append(targetRegions, apiObject) - } - - imageVersion := compute.GalleryImageVersion{ - Location: to.StringPtr(s.Location), - GalleryImageVersionProperties: &compute.GalleryImageVersionProperties{ - StorageProfile: &compute.GalleryImageVersionStorageProfile{ - OsDiskImage: &compute.GalleryOSDiskImage{ - Source: &compute.GalleryArtifactVersionSource{ID: to.StringPtr(snapshotset.OS().String())}, - HostCaching: compute.HostCaching(s.OSDiskCacheType), - }, - }, - PublishingProfile: &compute.GalleryImageVersionPublishingProfile{ - TargetRegions: &targetRegions, - ExcludeFromLatest: to.BoolPtr(s.Destination.ExcludeFromLatest), - }, - }, - } - - var datadisks []compute.GalleryDataDiskImage - for lun, resource := range snapshotset { - if lun != -1 { - ui.Say(fmt.Sprintf(" using %q for data disk (lun %d).", resource, lun)) - - datadisks = append(datadisks, compute.GalleryDataDiskImage{ - Lun: to.Int32Ptr(lun), - Source: &compute.GalleryArtifactVersionSource{ID: to.StringPtr(resource.String())}, - HostCaching: compute.HostCaching(s.DataDiskCacheType), - }) - } - } - if datadisks != nil { - // sort by lun - sort.Slice(datadisks, func(i, j int) bool { - return *datadisks[i].Lun < *datadisks[j].Lun - }) - imageVersion.GalleryImageVersionProperties.StorageProfile.DataDiskImages = &datadisks - } - - f, err := azcli.GalleryImageVersionsClient().CreateOrUpdate( - ctx, - s.Destination.ResourceGroup, - s.Destination.GalleryName, - s.Destination.ImageName, - s.Destination.ImageVersion, - imageVersion) - if err == nil { - log.Println("Shared image version creation in process...") - pollClient := azcli.PollClient() - pollClient.PollingDelay = 10 * time.Second - ctx, cancel := context.WithTimeout(ctx, time.Hour*12) - defer cancel() - err = f.WaitForCompletionRef(ctx, pollClient) - } - if err != nil { - log.Printf("StepCreateSharedImageVersion.Run: error: %+v", err) - err := fmt.Errorf( - "error creating shared image version '%s': %v", s.Destination.ResourceID(azcli.SubscriptionID()), err) - state.Put("error", err) - ui.Error(err.Error()) - return multistep.ActionHalt - } - log.Printf("Image creation complete: %s", f.Status()) - - return multistep.ActionContinue -} - -func (*StepCreateSharedImageVersion) Cleanup(multistep.StateBag) {} diff --git a/builder/azure/chroot/step_create_shared_image_version_test.go b/builder/azure/chroot/step_create_shared_image_version_test.go deleted file mode 100644 index bdaee7618..000000000 --- a/builder/azure/chroot/step_create_shared_image_version_test.go +++ /dev/null @@ -1,144 +0,0 @@ -package chroot - -import ( - "context" - "io/ioutil" - "net/http" - "reflect" - "regexp" - "testing" - - "github.com/hashicorp/packer-plugin-sdk/multistep" - packersdk "github.com/hashicorp/packer-plugin-sdk/packer" - "github.com/hashicorp/packer/builder/azure/common/client" - - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-12-01/compute" - "github.com/Azure/go-autorest/autorest" -) - -func TestStepCreateSharedImageVersion_Run(t *testing.T) { - type fields struct { - Destination SharedImageGalleryDestination - OSDiskCacheType string - DataDiskCacheType string - Location string - } - tests := []struct { - name string - fields fields - snapshotset Diskset - want multistep.StepAction - expectedPutBody string - }{ - { - name: "happy path", - fields: fields{ - Destination: SharedImageGalleryDestination{ - ResourceGroup: "ResourceGroup", - GalleryName: "GalleryName", - ImageName: "ImageName", - ImageVersion: "0.1.2", - TargetRegions: []TargetRegion{ - { - Name: "region1", - ReplicaCount: 5, - StorageAccountType: "Standard_ZRS", - }, - }, - ExcludeFromLatest: true, - }, - OSDiskCacheType: "ReadWrite", - DataDiskCacheType: "None", - Location: "region2", - }, - snapshotset: diskset( - "/subscriptions/12345/resourceGroups/group1/providers/Microsoft.Compute/snapshots/osdisksnapshot", - "/subscriptions/12345/resourceGroups/group1/providers/Microsoft.Compute/snapshots/datadisksnapshot0", - "/subscriptions/12345/resourceGroups/group1/providers/Microsoft.Compute/snapshots/datadisksnapshot1", - "/subscriptions/12345/resourceGroups/group1/providers/Microsoft.Compute/snapshots/datadisksnapshot2"), - expectedPutBody: `{ - "location": "region2", - "properties": { - "publishingProfile": { - "targetRegions": [ - { - "name": "region1", - "regionalReplicaCount": 5, - "storageAccountType": "Standard_ZRS" - } - ], - "excludeFromLatest": true - }, - "storageProfile": { - "osDiskImage": { - "hostCaching": "ReadWrite", - "source": { - "id": "/subscriptions/12345/resourceGroups/group1/providers/Microsoft.Compute/snapshots/osdisksnapshot" - } - }, - "dataDiskImages": [ - { - "lun": 0, - "hostCaching": "None", - "source": { - "id": "/subscriptions/12345/resourceGroups/group1/providers/Microsoft.Compute/snapshots/datadisksnapshot0" - } - }, - { - "lun": 1, - "hostCaching": "None", - "source": { - "id": "/subscriptions/12345/resourceGroups/group1/providers/Microsoft.Compute/snapshots/datadisksnapshot1" - } - }, - { - "lun": 2, - "hostCaching": "None", - "source": { - "id": "/subscriptions/12345/resourceGroups/group1/providers/Microsoft.Compute/snapshots/datadisksnapshot2" - } - } - ] - } - } - }`, - }, - } - for _, tt := range tests { - expectedPutBody := regexp.MustCompile(`[\s\n]`).ReplaceAllString(tt.expectedPutBody, "") - - m := compute.NewGalleryImageVersionsClient("subscriptionId") - m.Sender = autorest.SenderFunc(func(r *http.Request) (*http.Response, error) { - if r.Method != "PUT" { - t.Fatal("Expected only a PUT call") - } - b, _ := ioutil.ReadAll(r.Body) - if string(b) != expectedPutBody { - t.Errorf("expected body to be %v, but got %v", expectedPutBody, string(b)) - } - return &http.Response{ - Request: r, - StatusCode: 200, - }, nil - }) - - state := new(multistep.BasicStateBag) - state.Put("azureclient", &client.AzureClientSetMock{ - GalleryImageVersionsClientMock: m, - }) - state.Put("ui", packersdk.TestUi(t)) - state.Put(stateBagKey_Snapshotset, tt.snapshotset) - - t.Run(tt.name, func(t *testing.T) { - s := &StepCreateSharedImageVersion{ - Destination: tt.fields.Destination, - OSDiskCacheType: tt.fields.OSDiskCacheType, - DataDiskCacheType: tt.fields.DataDiskCacheType, - Location: tt.fields.Location, - } - if got := s.Run(context.TODO(), state); !reflect.DeepEqual(got, tt.want) { - t.Errorf("StepCreateSharedImageVersion.Run() = %v, want %v", got, tt.want) - } - }) - } -} diff --git a/builder/azure/chroot/step_create_snapshotset.go b/builder/azure/chroot/step_create_snapshotset.go deleted file mode 100644 index 2e67c6660..000000000 --- a/builder/azure/chroot/step_create_snapshotset.go +++ /dev/null @@ -1,126 +0,0 @@ -package chroot - -import ( - "context" - "fmt" - "log" - "strings" - "time" - - "github.com/hashicorp/packer-plugin-sdk/multistep" - packersdk "github.com/hashicorp/packer-plugin-sdk/packer" - "github.com/hashicorp/packer/builder/azure/common/client" - - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-12-01/compute" - "github.com/Azure/go-autorest/autorest/to" -) - -var _ multistep.Step = &StepCreateSnapshotset{} - -type StepCreateSnapshotset struct { - OSDiskSnapshotID string - DataDiskSnapshotIDPrefix string - Location string - - SkipCleanup bool - - snapshots Diskset -} - -func (s *StepCreateSnapshotset) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { - azcli := state.Get("azureclient").(client.AzureClientSet) - ui := state.Get("ui").(packersdk.Ui) - diskset := state.Get(stateBagKey_Diskset).(Diskset) - - s.snapshots = make(Diskset) - - errorMessage := func(format string, params ...interface{}) multistep.StepAction { - err := fmt.Errorf("StepCreateSnapshotset.Run: error: "+format, params...) - state.Put("error", err) - ui.Error(err.Error()) - return multistep.ActionHalt - } - - for lun, resource := range diskset { - snapshotID := fmt.Sprintf("%s%d", s.DataDiskSnapshotIDPrefix, lun) - if lun == -1 { - snapshotID = s.OSDiskSnapshotID - } - ssr, err := client.ParseResourceID(snapshotID) - if err != nil { - errorMessage("Could not create a valid resource id, tried %q: %v", snapshotID, err) - } - if !strings.EqualFold(ssr.Provider, "Microsoft.Compute") || - !strings.EqualFold(ssr.ResourceType.String(), "snapshots") { - return errorMessage("Resource %q is not of type Microsoft.Compute/snapshots", snapshotID) - } - s.snapshots[lun] = ssr - state.Put(stateBagKey_Snapshotset, s.snapshots) - - ui.Say(fmt.Sprintf("Creating snapshot %q", ssr)) - - snapshot := compute.Snapshot{ - Location: to.StringPtr(s.Location), - SnapshotProperties: &compute.SnapshotProperties{ - CreationData: &compute.CreationData{ - CreateOption: compute.Copy, - SourceResourceID: to.StringPtr(resource.String()), - }, - Incremental: to.BoolPtr(false), - }, - } - - f, err := azcli.SnapshotsClient().CreateOrUpdate(ctx, ssr.ResourceGroup, ssr.ResourceName.String(), snapshot) - if err != nil { - return errorMessage("error initiating snapshot %q: %v", ssr, err) - } - - pollClient := azcli.PollClient() - pollClient.PollingDelay = 2 * time.Second - ctx, cancel := context.WithTimeout(ctx, time.Hour*12) - defer cancel() - err = f.WaitForCompletionRef(ctx, pollClient) - - if err != nil { - return errorMessage("error creating snapshot '%s': %v", s.OSDiskSnapshotID, err) - } - } - - return multistep.ActionContinue -} - -func (s *StepCreateSnapshotset) Cleanup(state multistep.StateBag) { - if !s.SkipCleanup { - azcli := state.Get("azureclient").(client.AzureClientSet) - ui := state.Get("ui").(packersdk.Ui) - - for _, resource := range s.snapshots { - - ui.Say(fmt.Sprintf("Removing any active SAS for snapshot %q", resource)) - { - f, err := azcli.SnapshotsClient().RevokeAccess(context.TODO(), resource.ResourceGroup, resource.ResourceName.String()) - if err == nil { - log.Printf("StepCreateSnapshotset.Cleanup: removing SAS...") - err = f.WaitForCompletionRef(context.TODO(), azcli.PollClient()) - } - if err != nil { - log.Printf("StepCreateSnapshotset.Cleanup: error: %+v", err) - ui.Error(fmt.Sprintf("error deleting snapshot %q: %v.", resource, err)) - } - } - - ui.Say(fmt.Sprintf("Deleting snapshot %q", resource)) - { - f, err := azcli.SnapshotsClient().Delete(context.TODO(), resource.ResourceGroup, resource.ResourceName.String()) - if err == nil { - log.Printf("StepCreateSnapshotset.Cleanup: deleting snapshot...") - err = f.WaitForCompletionRef(context.TODO(), azcli.PollClient()) - } - if err != nil { - log.Printf("StepCreateSnapshotset.Cleanup: error: %+v", err) - ui.Error(fmt.Sprintf("error deleting snapshot %q: %v.", resource, err)) - } - } - } - } -} diff --git a/builder/azure/chroot/step_create_snapshotset_test.go b/builder/azure/chroot/step_create_snapshotset_test.go deleted file mode 100644 index 73a183353..000000000 --- a/builder/azure/chroot/step_create_snapshotset_test.go +++ /dev/null @@ -1,230 +0,0 @@ -package chroot - -import ( - "context" - "io/ioutil" - "net/http" - "reflect" - "regexp" - "testing" - - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-12-01/compute" - "github.com/Azure/go-autorest/autorest" - "github.com/hashicorp/packer-plugin-sdk/multistep" - packersdk "github.com/hashicorp/packer-plugin-sdk/packer" - "github.com/hashicorp/packer/builder/azure/common/client" -) - -func TestStepCreateSnapshot_Run(t *testing.T) { - type fields struct { - OSDiskSnapshotID string - DataDiskSnapshotIDPrefix string - Location string - } - tests := []struct { - name string - fields fields - diskset Diskset - want multistep.StepAction - wantSnapshotset Diskset - expectedPutBody string - }{ - { - name: "happy path", - fields: fields{ - OSDiskSnapshotID: "/subscriptions/1234/resourceGroups/rg/providers/Microsoft.Compute/snapshots/osdisk-snap", - Location: "region1", - }, - diskset: diskset("/subscriptions/12345/resourceGroups/group1/providers/Microsoft.Compute/disks/disk1"), - expectedPutBody: `{ - "location": "region1", - "properties": { - "creationData": { - "createOption": "Copy", - "sourceResourceId": "/subscriptions/12345/resourceGroups/group1/providers/Microsoft.Compute/disks/disk1" - }, - "incremental": false - } - }`, - wantSnapshotset: diskset("/subscriptions/1234/resourceGroups/rg/providers/Microsoft.Compute/snapshots/osdisk-snap"), - }, - { - name: "multi disk", - fields: fields{ - OSDiskSnapshotID: "/subscriptions/1234/resourceGroups/rg/providers/Microsoft.Compute/snapshots/osdisk-snap", - DataDiskSnapshotIDPrefix: "/subscriptions/1234/resourceGroups/rg/providers/Microsoft.Compute/snapshots/datadisk-snap", - Location: "region1", - }, - diskset: diskset( - "/subscriptions/12345/resourceGroups/group1/providers/Microsoft.Compute/disks/osdisk", - "/subscriptions/12345/resourceGroups/group1/providers/Microsoft.Compute/disks/datadisk1", - "/subscriptions/12345/resourceGroups/group1/providers/Microsoft.Compute/disks/datadisk2", - "/subscriptions/12345/resourceGroups/group1/providers/Microsoft.Compute/disks/datadisk3"), - wantSnapshotset: diskset( - "/subscriptions/1234/resourceGroups/rg/providers/Microsoft.Compute/snapshots/osdisk-snap", - "/subscriptions/1234/resourceGroups/rg/providers/Microsoft.Compute/snapshots/datadisk-snap0", - "/subscriptions/1234/resourceGroups/rg/providers/Microsoft.Compute/snapshots/datadisk-snap1", - "/subscriptions/1234/resourceGroups/rg/providers/Microsoft.Compute/snapshots/datadisk-snap2", - ), - }, - { - name: "invalid ResourceID", - fields: fields{ - OSDiskSnapshotID: "notaresourceid", - Location: "region1", - }, - diskset: diskset("/subscriptions/12345/resourceGroups/group1/providers/Microsoft.Compute/disks/disk1"), - want: multistep.ActionHalt, - }, - } - for _, tt := range tests { - expectedPutBody := regexp.MustCompile(`[\s\n]`).ReplaceAllString(tt.expectedPutBody, "") - - m := compute.NewSnapshotsClient("subscriptionId") - m.Sender = autorest.SenderFunc(func(r *http.Request) (*http.Response, error) { - if r.Method != "PUT" { - t.Fatal("Expected only a PUT call") - } - if expectedPutBody != "" { - b, _ := ioutil.ReadAll(r.Body) - if string(b) != expectedPutBody { - t.Errorf("expected body to be %v, but got %v", expectedPutBody, string(b)) - } - } - return &http.Response{ - Request: r, - StatusCode: 200, - }, nil - }) - - state := new(multistep.BasicStateBag) - state.Put("azureclient", &client.AzureClientSetMock{ - SnapshotsClientMock: m, - }) - state.Put("ui", packersdk.TestUi(t)) - state.Put(stateBagKey_Diskset, tt.diskset) - - t.Run(tt.name, func(t *testing.T) { - s := &StepCreateSnapshotset{ - OSDiskSnapshotID: tt.fields.OSDiskSnapshotID, - DataDiskSnapshotIDPrefix: tt.fields.DataDiskSnapshotIDPrefix, - Location: tt.fields.Location, - } - if got := s.Run(context.TODO(), state); !reflect.DeepEqual(got, tt.want) { - t.Errorf("StepCreateSnapshot.Run() = %v, want %v", got, tt.want) - } - - if len(tt.wantSnapshotset) > 0 { - got := state.Get(stateBagKey_Snapshotset).(Diskset) - if !reflect.DeepEqual(got, tt.wantSnapshotset) { - t.Errorf("Snapshotset = %v, want %v", got, tt.wantSnapshotset) - } - } - - }) - } -} - -func TestStepCreateSnapshot_Cleanup_skipped(t *testing.T) { - m := compute.NewSnapshotsClient("subscriptionId") - m.Sender = autorest.SenderFunc(func(r *http.Request) (*http.Response, error) { - t.Fatalf("clean up should be skipped, did not expect HTTP calls") - return nil, nil - }) - - state := new(multistep.BasicStateBag) - state.Put("azureclient", &client.AzureClientSetMock{ - SnapshotsClientMock: m, - }) - state.Put("ui", packersdk.TestUi(t)) - - s := &StepCreateSnapshotset{ - SkipCleanup: true, - } - s.Cleanup(state) -} - -func TestStepCreateSnapshot_Cleanup(t *testing.T) { - m := compute.NewSnapshotsClient("subscriptionId") - { - expectedCalls := []string{ - "POST /subscriptions/subscriptionId/resourceGroups/rg/providers/Microsoft.Compute/snapshots/ossnap/endGetAccess", - "DELETE /subscriptions/subscriptionId/resourceGroups/rg/providers/Microsoft.Compute/snapshots/ossnap", - "POST /subscriptions/subscriptionId/resourceGroups/rg/providers/Microsoft.Compute/snapshots/datasnap1/endGetAccess", - "DELETE /subscriptions/subscriptionId/resourceGroups/rg/providers/Microsoft.Compute/snapshots/datasnap1", - "POST /subscriptions/subscriptionId/resourceGroups/rg/providers/Microsoft.Compute/snapshots/datasnap2/endGetAccess", - "DELETE /subscriptions/subscriptionId/resourceGroups/rg/providers/Microsoft.Compute/snapshots/datasnap2", - } - - m.Sender = autorest.SenderFunc(func(r *http.Request) (*http.Response, error) { - got := r.Method + " " + r.URL.Path - found := false - for i, call := range expectedCalls { - if call == got { - // swap i with last and drop last - expectedCalls[i] = expectedCalls[len(expectedCalls)-1] - expectedCalls = expectedCalls[:len(expectedCalls)-1] - found = true - break - } - } - if !found { - t.Errorf("unexpected HTTP call: %v, wanted one of %q", got, expectedCalls) - return &http.Response{ - Request: r, - StatusCode: 599, // 500 is retried - }, nil - } - return &http.Response{ - Request: r, - StatusCode: 200, - }, nil - }) - } - state := new(multistep.BasicStateBag) - state.Put("azureclient", &client.AzureClientSetMock{ - SnapshotsClientMock: m, - }) - state.Put("ui", packersdk.TestUi(t)) - - s := &StepCreateSnapshotset{ - SkipCleanup: false, - snapshots: diskset( - "/subscriptions/1234/resourceGroups/rg/providers/Microsoft.Compute/snapshots/ossnap", - "/subscriptions/1234/resourceGroups/rg/providers/Microsoft.Compute/snapshots/datasnap1", - "/subscriptions/1234/resourceGroups/rg/providers/Microsoft.Compute/snapshots/datasnap2"), - } - s.Cleanup(state) -} - -func TestStepCreateSnapshotset_Cleanup(t *testing.T) { - type fields struct { - OSDiskSnapshotID string - DataDiskSnapshotIDPrefix string - Location string - SkipCleanup bool - snapshots Diskset - } - type args struct { - state multistep.StateBag - } - tests := []struct { - name string - fields fields - args args - }{ - // TODO: Add test cases. - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - s := &StepCreateSnapshotset{ - OSDiskSnapshotID: tt.fields.OSDiskSnapshotID, - DataDiskSnapshotIDPrefix: tt.fields.DataDiskSnapshotIDPrefix, - Location: tt.fields.Location, - SkipCleanup: tt.fields.SkipCleanup, - snapshots: tt.fields.snapshots, - } - s.Cleanup(tt.args.state) - }) - } -} diff --git a/builder/azure/chroot/step_mount_device.go b/builder/azure/chroot/step_mount_device.go deleted file mode 100644 index 1944ffb51..000000000 --- a/builder/azure/chroot/step_mount_device.go +++ /dev/null @@ -1,140 +0,0 @@ -package chroot - -// mostly borrowed from ./builder/amazon/chroot/step_mount_device.go - -import ( - "bytes" - "context" - "fmt" - "log" - "os" - "path/filepath" - "runtime" - "strings" - - "github.com/hashicorp/packer-plugin-sdk/common" - "github.com/hashicorp/packer-plugin-sdk/multistep" - packersdk "github.com/hashicorp/packer-plugin-sdk/packer" - "github.com/hashicorp/packer-plugin-sdk/template/interpolate" -) - -var _ multistep.Step = &StepMountDevice{} - -type StepMountDevice struct { - MountOptions []string - MountPartition string - MountPath string - - mountPath string -} - -func (s *StepMountDevice) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { - ui := state.Get("ui").(packersdk.Ui) - device := state.Get("device").(string) - config := state.Get("config").(*Config) - wrappedCommand := state.Get("wrappedCommand").(common.CommandWrapper) - - ictx := config.ctx - - ictx.Data = &struct{ Device string }{Device: filepath.Base(device)} - mountPath, err := interpolate.Render(s.MountPath, &ictx) - - if err != nil { - err := fmt.Errorf("error preparing mount directory: %s", err) - state.Put("error", err) - ui.Error(err.Error()) - return multistep.ActionHalt - } - - mountPath, err = filepath.Abs(mountPath) - if err != nil { - err := fmt.Errorf("error preparing mount directory: %s", err) - state.Put("error", err) - ui.Error(err.Error()) - return multistep.ActionHalt - } - - log.Printf("Mount path: %s", mountPath) - - if err := os.MkdirAll(mountPath, 0755); err != nil { - err := fmt.Errorf("error creating mount directory: %s", err) - state.Put("error", err) - ui.Error(err.Error()) - return multistep.ActionHalt - } - - var deviceMount string - switch runtime.GOOS { - case "freebsd": - deviceMount = fmt.Sprintf("%sp%s", device, s.MountPartition) - default: - deviceMount = fmt.Sprintf("%s%s", device, s.MountPartition) - } - - state.Put("deviceMount", deviceMount) - - ui.Say("Mounting the root device...") - stderr := new(bytes.Buffer) - - // build mount options from mount_options config, useful for nouuid options - // or other specific device type settings for mount - opts := "" - if len(s.MountOptions) > 0 { - opts = "-o " + strings.Join(s.MountOptions, " -o ") - } - mountCommand, err := wrappedCommand( - fmt.Sprintf("mount %s %s %s", opts, deviceMount, mountPath)) - if err != nil { - err := fmt.Errorf("error creating mount command: %s", err) - state.Put("error", err) - ui.Error(err.Error()) - return multistep.ActionHalt - } - log.Printf("[DEBUG] (step mount) mount command is %s", mountCommand) - cmd := common.ShellCommand(mountCommand) - cmd.Stderr = stderr - if err := cmd.Run(); err != nil { - err := fmt.Errorf( - "error mounting root volume: %s\nStderr: %s", err, stderr.String()) - state.Put("error", err) - ui.Error(err.Error()) - return multistep.ActionHalt - } - - // Set the mount path so we remember to unmount it later - s.mountPath = mountPath - state.Put("mount_path", s.mountPath) - state.Put("mount_device_cleanup", s) - - return multistep.ActionContinue -} - -func (s *StepMountDevice) Cleanup(state multistep.StateBag) { - ui := state.Get("ui").(packersdk.Ui) - if err := s.CleanupFunc(state); err != nil { - ui.Error(err.Error()) - } -} - -func (s *StepMountDevice) CleanupFunc(state multistep.StateBag) error { - if s.mountPath == "" { - return nil - } - - ui := state.Get("ui").(packersdk.Ui) - wrappedCommand := state.Get("wrappedCommand").(common.CommandWrapper) - - ui.Say("Unmounting the root device...") - unmountCommand, err := wrappedCommand(fmt.Sprintf("umount %s", s.mountPath)) - if err != nil { - return fmt.Errorf("error creating unmount command: %s", err) - } - - cmd := common.ShellCommand(unmountCommand) - if err := cmd.Run(); err != nil { - return fmt.Errorf("error unmounting root device: %s", err) - } - - s.mountPath = "" - return nil -} diff --git a/builder/azure/chroot/step_mount_device_test.go b/builder/azure/chroot/step_mount_device_test.go deleted file mode 100644 index b1fb07355..000000000 --- a/builder/azure/chroot/step_mount_device_test.go +++ /dev/null @@ -1,72 +0,0 @@ -package chroot - -import ( - "context" - "fmt" - "io/ioutil" - "os" - "runtime" - "testing" - "time" - - "github.com/hashicorp/packer-plugin-sdk/common" - "github.com/hashicorp/packer-plugin-sdk/multistep" -) - -func TestStepMountDevice_Run(t *testing.T) { - switch runtime.GOOS { - case "linux", "freebsd": - break - default: - t.Skip("Unsupported operating system") - } - mountPath, err := ioutil.TempDir("", "stepmountdevicetest") - if err != nil { - t.Errorf("Unable to create a temporary directory: %q", err) - } - step := &StepMountDevice{ - MountOptions: []string{"foo"}, - MountPartition: "42", - MountPath: mountPath, - } - - var gotCommand string - var wrapper common.CommandWrapper - wrapper = func(ran string) (string, error) { - gotCommand = ran - return "", nil - } - - state := new(multistep.BasicStateBag) - state.Put("wrappedCommand", wrapper) - state.Put("device", "/dev/quux") - - ui, getErrs := testUI() - state.Put("ui", ui) - - var config Config - state.Put("config", &config) - - ctx, cancel := context.WithTimeout(context.Background(), time.Second) - defer cancel() - - got := step.Run(ctx, state) - if got != multistep.ActionContinue { - t.Errorf("Expected 'continue', but got '%v'", got) - } - - var expectedMountDevice string - switch runtime.GOOS { - case "freebsd": - expectedMountDevice = "/dev/quuxp42" - default: // currently just Linux - expectedMountDevice = "/dev/quux42" - } - expectedCommand := fmt.Sprintf("mount -o foo %s %s", expectedMountDevice, mountPath) - if gotCommand != expectedCommand { - t.Errorf("Expected '%v', but got '%v'", expectedCommand, gotCommand) - } - - os.Remove(mountPath) - _ = getErrs -} diff --git a/builder/azure/chroot/step_resolve_plaform_image_version.go b/builder/azure/chroot/step_resolve_plaform_image_version.go deleted file mode 100644 index 677eb016c..000000000 --- a/builder/azure/chroot/step_resolve_plaform_image_version.go +++ /dev/null @@ -1,45 +0,0 @@ -package chroot - -import ( - "context" - "fmt" - "log" - "strings" - - "github.com/Azure/go-autorest/autorest/to" - "github.com/hashicorp/packer-plugin-sdk/multistep" - packersdk "github.com/hashicorp/packer-plugin-sdk/packer" - "github.com/hashicorp/packer/builder/azure/common/client" -) - -// StepResolvePlatformImageVersion resolves the exact PIR version when the version is 'latest' -type StepResolvePlatformImageVersion struct { - *client.PlatformImage - Location string -} - -// Run retrieves all available versions of a PIR image and stores the latest in the PlatformImage -func (pi *StepResolvePlatformImageVersion) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { - ui := state.Get("ui").(packersdk.Ui) - - if strings.EqualFold(pi.Version, "latest") { - azcli := state.Get("azureclient").(client.AzureClientSet) - - vmi, err := azcli.VirtualMachineImagesClient().GetLatest(ctx, pi.Publisher, pi.Offer, pi.Sku, pi.Location) - if err != nil { - log.Printf("StepResolvePlatformImageVersion.Run: error: %+v", err) - err := fmt.Errorf("error retieving latest version of %q: %v", pi.URN(), err) - state.Put("error", err) - ui.Error(err.Error()) - return multistep.ActionHalt - } - pi.Version = to.String(vmi.Name) - ui.Say("Resolved latest version of source image: " + pi.Version) - } else { - ui.Say("Nothing to do, version is not 'latest'") - } - - return multistep.ActionContinue -} - -func (*StepResolvePlatformImageVersion) Cleanup(multistep.StateBag) {} diff --git a/builder/azure/chroot/step_resolve_plaform_image_version_test.go b/builder/azure/chroot/step_resolve_plaform_image_version_test.go deleted file mode 100644 index 60b4e2791..000000000 --- a/builder/azure/chroot/step_resolve_plaform_image_version_test.go +++ /dev/null @@ -1,62 +0,0 @@ -package chroot - -import ( - "context" - "io/ioutil" - "net/http" - "strings" - "testing" - "time" - - "github.com/hashicorp/packer-plugin-sdk/multistep" - "github.com/hashicorp/packer/builder/azure/common/client" - - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-12-01/compute" - "github.com/Azure/go-autorest/autorest" -) - -func TestStepResolvePlatformImageVersion_Run(t *testing.T) { - - pi := &StepResolvePlatformImageVersion{ - PlatformImage: &client.PlatformImage{ - Version: "latest", - }} - - m := compute.NewVirtualMachineImagesClient("subscriptionId") - m.Sender = autorest.SenderFunc(func(r *http.Request) (*http.Response, error) { - if !strings.Contains(r.URL.String(), "%24orderby=name+desc") { - t.Errorf("Expected url to use odata based sorting, but got %q", r.URL.String()) - } - return &http.Response{ - Request: r, - Body: ioutil.NopCloser(strings.NewReader( - `[ - {"name":"1.2.3"}, - {"name":"4.5.6"} - ]`)), - StatusCode: 200, - }, nil - }) - - state := new(multistep.BasicStateBag) - state.Put("azureclient", &client.AzureClientSetMock{ - VirtualMachineImagesClientMock: client.VirtualMachineImagesClient{ - VirtualMachineImagesClientAPI: m}}) - - ui, getErrs := testUI() - state.Put("ui", ui) - - ctx, cancel := context.WithTimeout(context.Background(), time.Second) - defer cancel() - - got := pi.Run(ctx, state) - if got != multistep.ActionContinue { - t.Errorf("Expected 'continue', but got %q", got) - } - - if pi.PlatformImage.Version != "1.2.3" { - t.Errorf("Expected version '1.2.3', but got %q", pi.PlatformImage.Version) - } - - _ = getErrs -} diff --git a/builder/azure/chroot/step_verify_shared_image_destination.go b/builder/azure/chroot/step_verify_shared_image_destination.go deleted file mode 100644 index a05103b9c..000000000 --- a/builder/azure/chroot/step_verify_shared_image_destination.go +++ /dev/null @@ -1,119 +0,0 @@ -package chroot - -import ( - "context" - "fmt" - "log" - "strings" - - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-12-01/compute" - "github.com/Azure/go-autorest/autorest/to" - "github.com/hashicorp/packer-plugin-sdk/multistep" - packersdk "github.com/hashicorp/packer-plugin-sdk/packer" - "github.com/hashicorp/packer/builder/azure/common/client" -) - -var _ multistep.Step = &StepVerifySharedImageDestination{} - -// StepVerifySharedImageDestination verifies that the shared image location matches the Location field in the step. -// Also verifies that the OS Type is Linux. -type StepVerifySharedImageDestination struct { - Image SharedImageGalleryDestination - Location string -} - -// Run retrieves the image metadata from Azure and compares the location to Location. Verifies the OS Type. -func (s *StepVerifySharedImageDestination) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { - azcli := state.Get("azureclient").(client.AzureClientSet) - ui := state.Get("ui").(packersdk.Ui) - - errorMessage := func(message string, parameters ...interface{}) multistep.StepAction { - err := fmt.Errorf(message, parameters...) - log.Printf("StepVerifySharedImageDestination.Run: error: %+v", err) - state.Put("error", err) - ui.Error(err.Error()) - return multistep.ActionHalt - } - - imageURI := fmt.Sprintf("/subscriptions/%s/resourcegroup/%s/providers/Microsoft.Compute/galleries/%s/images/%s", - azcli.SubscriptionID(), - s.Image.ResourceGroup, - s.Image.GalleryName, - s.Image.ImageName, - ) - - ui.Say(fmt.Sprintf("Validating that shared image %s exists", - imageURI)) - - image, err := azcli.GalleryImagesClient().Get(ctx, - s.Image.ResourceGroup, - s.Image.GalleryName, - s.Image.ImageName) - - if err != nil { - return errorMessage("Error retrieving shared image %q: %+v ", imageURI, err) - } - - if image.ID == nil || *image.ID == "" { - return errorMessage("Error retrieving shared image %q: ID field in response is empty", imageURI) - } - if image.GalleryImageProperties == nil { - return errorMessage("Could not retrieve shared image properties for image %q.", to.String(image.ID)) - } - - location := to.String(image.Location) - - log.Printf("StepVerifySharedImageDestination:Run: Image %q, Location: %q, HvGen: %q, osState: %q", - to.String(image.ID), - location, - image.GalleryImageProperties.HyperVGeneration, - image.GalleryImageProperties.OsState) - - if !strings.EqualFold(location, s.Location) { - return errorMessage("Destination shared image resource %q is in a different location (%q) than this VM (%q). "+ - "Packer does not know how to handle that.", - to.String(image.ID), - location, - s.Location) - } - - if image.GalleryImageProperties.OsType != compute.Linux { - return errorMessage("The shared image (%q) is not a Linux image (found %q). Currently only Linux images are supported.", - to.String(image.ID), - image.GalleryImageProperties.OsType) - } - - ui.Say(fmt.Sprintf("Found image %s in location %s", - to.String(image.ID), - to.String(image.Location))) - - versions, err := azcli.GalleryImageVersionsClient().ListByGalleryImageComplete(ctx, - s.Image.ResourceGroup, - s.Image.GalleryName, - s.Image.ImageName) - - if err != nil { - return errorMessage("Could not ListByGalleryImageComplete group:%v gallery:%v image:%v", - s.Image.ResourceGroup, s.Image.GalleryName, s.Image.ImageName) - } - - for versions.NotDone() { - version := versions.Value() - - if version.Name == nil { - return errorMessage("Could not retrieve versions for image %q: unexpected nil name", to.String(image.ID)) - } - if *version.Name == s.Image.ImageVersion { - return errorMessage("Shared image version %q already exists for image %q.", s.Image.ImageVersion, to.String(image.ID)) - } - - err := versions.NextWithContext(ctx) - if err != nil { - return errorMessage("Could not retrieve versions for image %q: %+v", to.String(image.ID), err) - } - } - - return multistep.ActionContinue -} - -func (*StepVerifySharedImageDestination) Cleanup(multistep.StateBag) {} diff --git a/builder/azure/chroot/step_verify_shared_image_destination_test.go b/builder/azure/chroot/step_verify_shared_image_destination_test.go deleted file mode 100644 index 6df5d099b..000000000 --- a/builder/azure/chroot/step_verify_shared_image_destination_test.go +++ /dev/null @@ -1,186 +0,0 @@ -package chroot - -import ( - "context" - "io/ioutil" - "net/http" - "reflect" - "strings" - "testing" - - "github.com/hashicorp/packer-plugin-sdk/multistep" - packersdk "github.com/hashicorp/packer-plugin-sdk/packer" - "github.com/hashicorp/packer/builder/azure/common/client" - - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-12-01/compute" - "github.com/Azure/go-autorest/autorest" -) - -func TestStepVerifySharedImageDestination_Run(t *testing.T) { - - type fields struct { - Image SharedImageGalleryDestination - Location string - } - tests := []struct { - name string - fields fields - want multistep.StepAction - wantErr string - }{ - { - name: "happy path", - want: multistep.ActionContinue, - fields: fields{ - Image: SharedImageGalleryDestination{ - ResourceGroup: "rg", - GalleryName: "gallery", - ImageName: "image", - ImageVersion: "1.2.3", - }, - Location: "region1", - }, - }, - { - name: "not found", - want: multistep.ActionHalt, - wantErr: `Error retrieving shared image "/subscriptions/subscriptionID/resourcegroup/other-rg/providers/Microsoft.Compute/galleries/gallery/images/image": compute.GalleryImagesClient#Get: Failure responding to request: StatusCode=404 -- Original Error: autorest/azure: Service returned an error. Status=404 Code="NotFound" Message="Not found" `, - fields: fields{ - Image: SharedImageGalleryDestination{ - ResourceGroup: "other-rg", - GalleryName: "gallery", - ImageName: "image", - ImageVersion: "1.2.3", - }, - Location: "region1", - }, - }, - { - name: "wrong region", - want: multistep.ActionHalt, - wantErr: "Destination shared image resource \"image-resourceid-goes-here\" is in a different location (\"region1\") than this VM (\"other-region\"). Packer does not know how to handle that.", - fields: fields{ - Image: SharedImageGalleryDestination{ - ResourceGroup: "rg", - GalleryName: "gallery", - ImageName: "image", - ImageVersion: "1.2.3", - }, - Location: "other-region", - }, - }, - { - name: "version exists", - want: multistep.ActionHalt, - wantErr: "Shared image version \"2.3.4\" already exists for image \"image-resourceid-goes-here\".", - fields: fields{ - Image: SharedImageGalleryDestination{ - ResourceGroup: "rg", - GalleryName: "gallery", - ImageName: "image", - ImageVersion: "2.3.4", - }, - Location: "region1", - }, - }, - { - name: "not Linux", - want: multistep.ActionHalt, - wantErr: "The shared image (\"windows-image-resourceid-goes-here\") is not a Linux image (found \"Windows\"). Currently only Linux images are supported.", - fields: fields{ - Image: SharedImageGalleryDestination{ - ResourceGroup: "rg", - GalleryName: "gallery", - ImageName: "windowsimage", - ImageVersion: "1.2.3", - }, - Location: "region1", - }, - }, - } - for _, tt := range tests { - gi := compute.NewGalleryImagesClient("subscriptionID") - gi.Sender = autorest.SenderFunc(func(r *http.Request) (*http.Response, error) { - switch { - case r.Method == "GET" && strings.HasPrefix(r.URL.RequestURI(), - "/subscriptions/subscriptionID/resourceGroups/rg/providers/Microsoft.Compute/galleries/gallery/images/image"): - return &http.Response{ - Request: r, - Body: ioutil.NopCloser(strings.NewReader(`{ - "id": "image-resourceid-goes-here", - "location": "region1", - "properties": { - "osType": "Linux" - } - }`)), - StatusCode: 200, - }, nil - case r.Method == "GET" && strings.HasPrefix(r.URL.RequestURI(), - "/subscriptions/subscriptionID/resourceGroups/rg/providers/Microsoft.Compute/galleries/gallery/images/windowsimage"): - return &http.Response{ - Request: r, - Body: ioutil.NopCloser(strings.NewReader(`{ - "id": "windows-image-resourceid-goes-here", - "location": "region1", - "properties": { - "osType": "Windows" - } - }`)), - StatusCode: 200, - }, nil - } - return &http.Response{ - Request: r, - Body: ioutil.NopCloser(strings.NewReader(`{ - "Code": "NotFound", - "Message": "Not found" - }`)), - StatusCode: 404, - }, nil - }) - - giv := compute.NewGalleryImageVersionsClient("subscriptionID") - giv.Sender = autorest.SenderFunc(func(r *http.Request) (*http.Response, error) { - if !(r.Method == "GET" && strings.HasPrefix(r.URL.RequestURI(), - "/subscriptions/subscriptionID/resourceGroups/rg/providers/Microsoft.Compute/galleries/gallery/images/image/versions")) { - t.Errorf("Unexpected HTTP call: %s %s", r.Method, r.URL.RequestURI()) - } - return &http.Response{ - Request: r, - Body: ioutil.NopCloser(strings.NewReader(`{ - "value": [ - { - "name": "2.3.4" - } - ] - }`)), - StatusCode: 200, - }, nil - }) - - state := new(multistep.BasicStateBag) - state.Put("azureclient", &client.AzureClientSetMock{ - SubscriptionIDMock: "subscriptionID", - GalleryImagesClientMock: gi, - GalleryImageVersionsClientMock: giv, - }) - state.Put("ui", packersdk.TestUi(t)) - - t.Run(tt.name, func(t *testing.T) { - s := &StepVerifySharedImageDestination{ - Image: tt.fields.Image, - Location: tt.fields.Location, - } - if got := s.Run(context.TODO(), state); !reflect.DeepEqual(got, tt.want) { - t.Errorf("StepVerifySharedImageDestination.Run() = %v, want %v", got, tt.want) - } - }) - if err, ok := state.GetOk("error"); ok { - if err.(error).Error() != tt.wantErr { - t.Errorf("Unexpected error, got: %q, want: %q", err, tt.wantErr) - } - } else if tt.wantErr != "" { - t.Errorf("Expected error, but didn't get any") - } - } -} diff --git a/builder/azure/chroot/step_verify_shared_image_source.go b/builder/azure/chroot/step_verify_shared_image_source.go deleted file mode 100644 index 840160eff..000000000 --- a/builder/azure/chroot/step_verify_shared_image_source.go +++ /dev/null @@ -1,126 +0,0 @@ -package chroot - -import ( - "context" - "fmt" - "log" - "strings" - - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-12-01/compute" - "github.com/Azure/go-autorest/autorest/to" - "github.com/hashicorp/packer-plugin-sdk/multistep" - packersdk "github.com/hashicorp/packer-plugin-sdk/packer" - "github.com/hashicorp/packer/builder/azure/common/client" -) - -var _ multistep.Step = &StepVerifySharedImageSource{} - -// StepVerifySharedImageSource verifies that the shared image location matches the Location field in the step. -// Also verifies that the OS Type is Linux. -type StepVerifySharedImageSource struct { - SharedImageID string - SubscriptionID string - Location string -} - -// Run retrieves the image metadata from Azure and compares the location to Location. Verifies the OS Type. -func (s *StepVerifySharedImageSource) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { - azcli := state.Get("azureclient").(client.AzureClientSet) - ui := state.Get("ui").(packersdk.Ui) - - errorMessage := func(message string, parameters ...interface{}) multistep.StepAction { - err := fmt.Errorf(message, parameters...) - log.Printf("StepVerifySharedImageSource.Run: error: %+v", err) - state.Put("error", err) - ui.Error(err.Error()) - return multistep.ActionHalt - } - - resource, err := client.ParseResourceID(s.SharedImageID) - if err != nil { - return errorMessage("Could not parse resource id %q: %w", s.SharedImageID, err) - } - - if !strings.EqualFold(resource.Provider, "Microsoft.Compute") || - !strings.EqualFold(resource.ResourceType.String(), "galleries/images/versions") { - return errorMessage("Resource id %q does not identify a shared image version, expected Microsoft.Compute/galleries/images/versions", s.SharedImageID) - } - - ui.Say(fmt.Sprintf("Validating that shared image version %q exists", - s.SharedImageID)) - - version, err := azcli.GalleryImageVersionsClient().Get(ctx, - resource.ResourceGroup, - resource.ResourceName[0], - resource.ResourceName[1], - resource.ResourceName[2], - "") - - if err != nil { - return errorMessage("Error retrieving shared image version %q: %+v ", s.SharedImageID, err) - } - - if version.ID == nil || *version.ID == "" { - return errorMessage("Error retrieving shared image version %q: ID field in response is empty", s.SharedImageID) - } - - if version.GalleryImageVersionProperties == nil || - version.GalleryImageVersionProperties.PublishingProfile == nil || - version.GalleryImageVersionProperties.PublishingProfile.TargetRegions == nil { - return errorMessage("Could not retrieve shared image version properties for image %q.", s.SharedImageID) - } - - targetLocations := make([]string, 0, len(*version.GalleryImageVersionProperties.PublishingProfile.TargetRegions)) - vmLocation := client.NormalizeLocation(s.Location) - locationFound := false - for _, tr := range *version.GalleryImageVersionProperties.PublishingProfile.TargetRegions { - l := to.String(tr.Name) - l = client.NormalizeLocation(l) - targetLocations = append(targetLocations, l) - if strings.EqualFold(vmLocation, l) { - locationFound = true - break - } - } - if !locationFound { - return errorMessage("Target locations %q for %q does not include VM location %q", - targetLocations, s.SharedImageID, vmLocation) - } - - imageResource, _ := resource.Parent() - image, err := azcli.GalleryImagesClient().Get(ctx, - resource.ResourceGroup, - resource.ResourceName[0], - resource.ResourceName[1]) - - if err != nil { - return errorMessage("Error retrieving shared image %q: %+v ", imageResource.String(), err) - } - - if image.ID == nil || *image.ID == "" { - return errorMessage("Error retrieving shared image %q: ID field in response is empty", imageResource.String()) - } - - if image.GalleryImageProperties == nil { - return errorMessage("Could not retrieve shared image properties for image %q.", imageResource.String()) - } - - log.Printf("StepVerifySharedImageSource:Run: Image %q, HvGen: %q, osState: %q", - to.String(image.ID), - image.GalleryImageProperties.HyperVGeneration, - image.GalleryImageProperties.OsState) - - if image.GalleryImageProperties.OsType != compute.Linux { - return errorMessage("The shared image (%q) is not a Linux image (found %q). Currently only Linux images are supported.", - to.String(image.ID), - image.GalleryImageProperties.OsType) - } - - ui.Say(fmt.Sprintf("Found image source image version %q, available in location %s", - s.SharedImageID, - s.Location)) - - return multistep.ActionContinue -} - -func (*StepVerifySharedImageSource) Cleanup(multistep.StateBag) {} diff --git a/builder/azure/chroot/step_verify_shared_image_source_test.go b/builder/azure/chroot/step_verify_shared_image_source_test.go deleted file mode 100644 index 9950a6cf2..000000000 --- a/builder/azure/chroot/step_verify_shared_image_source_test.go +++ /dev/null @@ -1,188 +0,0 @@ -package chroot - -import ( - "context" - "io/ioutil" - "net/http" - "reflect" - "regexp" - "strings" - "testing" - - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-12-01/compute" - "github.com/Azure/go-autorest/autorest" - "github.com/hashicorp/packer-plugin-sdk/multistep" - packersdk "github.com/hashicorp/packer-plugin-sdk/packer" - "github.com/hashicorp/packer/builder/azure/common/client" -) - -func TestStepVerifySharedImageSource_Run(t *testing.T) { - type fields struct { - SharedImageID string - SubscriptionID string - Location string - } - tests := []struct { - name string - fields fields - want multistep.StepAction - wantErr string - }{ - { - name: "happy path", - fields: fields{ - SharedImageID: "/subscriptions/subscriptionID/resourceGroups/rg/providers/Microsoft.Compute/galleries/myGallery/images/myImage/versions/1.2.3", - Location: "VM location", - }, - }, - { - name: "resource is not a shared image", - fields: fields{ - SharedImageID: "/subscriptions/subscriptionID/resourceGroups/rg/providers/Microsoft.Compute/disks/myDisk", - Location: "VM location", - }, - want: multistep.ActionHalt, - wantErr: "does not identify a shared image version", - }, - { - name: "error in resource id", - fields: fields{ - SharedImageID: "not-a-resource-id", - }, - want: multistep.ActionHalt, - wantErr: "Could not parse resource id", - }, - { - name: "wrong location", - fields: fields{ - SharedImageID: "/subscriptions/subscriptionID/resourceGroups/rg/providers/Microsoft.Compute/galleries/myGallery/images/myImage/versions/1.2.3", - Location: "other location", - }, - want: multistep.ActionHalt, - wantErr: "does not include VM location", - }, - { - name: "image not found", - fields: fields{ - SharedImageID: "/subscriptions/subscriptionID/resourceGroups/rg/providers/Microsoft.Compute/galleries/myGallery/images/myImage/versions/2.3.4", - Location: "vm location", - }, - want: multistep.ActionHalt, - wantErr: "Error retrieving shared image version", - }, - { - name: "windows image", - fields: fields{ - SharedImageID: "/subscriptions/subscriptionID/resourceGroups/rg/providers/Microsoft.Compute/galleries/myGallery/images/windowsImage/versions/1.2.3", - Location: "VM location", - }, - want: multistep.ActionHalt, - wantErr: "not a Linux image", - }, - } - for _, tt := range tests { - giv := compute.NewGalleryImageVersionsClient("subscriptionID") - giv.Sender = autorest.SenderFunc(func(r *http.Request) (*http.Response, error) { - if r.Method == "GET" { - switch { - case strings.HasSuffix(r.URL.Path, "/versions/1.2.3"): - return &http.Response{ - Request: r, - Body: ioutil.NopCloser(strings.NewReader(`{ - "id": "image-version-id", - "properties": { - "publishingProfile": { - "targetRegions": [ - { "name": "vm Location" } - ] - } - } - }`)), - StatusCode: 200, - }, nil - case regexp.MustCompile(`(?i)^/subscriptions/subscriptionID/resourceGroups/rg/providers/Microsoft.Compute/galleries/myGallery/images/myImage/versions/\d+\.\d+\.\d+$`). - MatchString(r.URL.Path): - return &http.Response{ - Request: r, - Body: ioutil.NopCloser(strings.NewReader(`{"error":{"code":"NotFound"}}`)), - StatusCode: 404, - }, nil - } - } - - t.Errorf("Unexpected HTTP call: %s %s", r.Method, r.URL.RequestURI()) - return &http.Response{ - Request: r, - Status: "Unexpected HTTP call", - Body: ioutil.NopCloser(strings.NewReader(`{"code":"TestError"}`)), - StatusCode: 599, - }, nil - }) - - gi := compute.NewGalleryImagesClient("subscriptionID") - gi.Sender = autorest.SenderFunc(func(r *http.Request) (*http.Response, error) { - if r.Method == "GET" { - switch { - case strings.HasSuffix(r.URL.Path, "/images/myImage"): - return &http.Response{ - Request: r, - Body: ioutil.NopCloser(strings.NewReader(`{ - "id": "image-id", - "properties": { - "osType": "Linux" - } - }`)), - StatusCode: 200, - }, nil - case strings.HasSuffix(r.URL.Path, "/images/windowsImage"): - return &http.Response{ - Request: r, - Body: ioutil.NopCloser(strings.NewReader(`{ - "id": "image-id", - "properties": { - "osType": "Windows" - } - }`)), - StatusCode: 200, - }, nil - } - } - - t.Errorf("Unexpected HTTP call: %s %s", r.Method, r.URL.RequestURI()) - return &http.Response{ - Request: r, - Status: "Unexpected HTTP call", - Body: ioutil.NopCloser(strings.NewReader(`{"error":{"code":"TestError"}}`)), - StatusCode: 599, - }, nil - }) - - state := new(multistep.BasicStateBag) - state.Put("azureclient", &client.AzureClientSetMock{ - SubscriptionIDMock: "subscriptionID", - GalleryImageVersionsClientMock: giv, - GalleryImagesClientMock: gi, - }) - state.Put("ui", packersdk.TestUi(t)) - - t.Run(tt.name, func(t *testing.T) { - s := &StepVerifySharedImageSource{ - SharedImageID: tt.fields.SharedImageID, - SubscriptionID: tt.fields.SubscriptionID, - Location: tt.fields.Location, - } - if got := s.Run(context.TODO(), state); !reflect.DeepEqual(got, tt.want) { - t.Errorf("StepVerifySharedImageSource.Run() = %v, want %v", got, tt.want) - } - d, _ := state.GetOk("error") - err, _ := d.(error) - if tt.wantErr != "" { - if !strings.Contains(err.Error(), tt.wantErr) { - t.Errorf("Wanted error %q, got %q", tt.wantErr, err) - } - } else if err != nil && err.Error() != "" { - t.Errorf("Unexpected error: %v", err) - } - }) - } -} diff --git a/builder/azure/chroot/step_verify_source_disk.go b/builder/azure/chroot/step_verify_source_disk.go deleted file mode 100644 index 5f71fe415..000000000 --- a/builder/azure/chroot/step_verify_source_disk.go +++ /dev/null @@ -1,80 +0,0 @@ -package chroot - -import ( - "context" - "fmt" - "log" - "strings" - - "github.com/Azure/go-autorest/autorest/azure" - "github.com/Azure/go-autorest/autorest/to" - - "github.com/hashicorp/packer-plugin-sdk/multistep" - packersdk "github.com/hashicorp/packer-plugin-sdk/packer" - "github.com/hashicorp/packer/builder/azure/common/client" -) - -type StepVerifySourceDisk struct { - SourceDiskResourceID string - Location string -} - -func (s StepVerifySourceDisk) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { - azcli := state.Get("azureclient").(client.AzureClientSet) - ui := state.Get("ui").(packersdk.Ui) - - ui.Say("Checking source disk location") - resource, err := azure.ParseResourceID(s.SourceDiskResourceID) - if err != nil { - log.Printf("StepVerifySourceDisk.Run: error: %+v", err) - err := fmt.Errorf("Could not parse resource id %q: %s", s.SourceDiskResourceID, err) - state.Put("error", err) - ui.Error(err.Error()) - return multistep.ActionHalt - } - - if !strings.EqualFold(resource.SubscriptionID, azcli.SubscriptionID()) { - err := fmt.Errorf("Source disk resource %q is in a different subscription than this VM (%q). "+ - "Packer does not know how to handle that.", - s.SourceDiskResourceID, azcli.SubscriptionID()) - log.Printf("StepVerifySourceDisk.Run: error: %+v", err) - state.Put("error", err) - ui.Error(err.Error()) - return multistep.ActionHalt - } - - if !(strings.EqualFold(resource.Provider, "Microsoft.Compute") && strings.EqualFold(resource.ResourceType, "disks")) { - err := fmt.Errorf("Resource ID %q is not a managed disk resource", s.SourceDiskResourceID) - log.Printf("StepVerifySourceDisk.Run: error: %+v", err) - state.Put("error", err) - ui.Error(err.Error()) - return multistep.ActionHalt - } - - disk, err := azcli.DisksClient().Get(ctx, - resource.ResourceGroup, resource.ResourceName) - if err != nil { - err := fmt.Errorf("Unable to retrieve disk (%q): %s", s.SourceDiskResourceID, err) - log.Printf("StepVerifySourceDisk.Run: error: %+v", err) - state.Put("error", err) - ui.Error(err.Error()) - return multistep.ActionHalt - } - - location := to.String(disk.Location) - if !strings.EqualFold(location, s.Location) { - err := fmt.Errorf("Source disk resource %q is in a different location (%q) than this VM (%q). "+ - "Packer does not know how to handle that.", - s.SourceDiskResourceID, - location, - s.Location) - log.Printf("StepVerifySourceDisk.Run: error: %+v", err) - state.Put("error", err) - ui.Error(err.Error()) - return multistep.ActionHalt - } - - return multistep.ActionContinue -} - -func (s StepVerifySourceDisk) Cleanup(state multistep.StateBag) {} diff --git a/builder/azure/chroot/step_verify_source_disk_test.go b/builder/azure/chroot/step_verify_source_disk_test.go deleted file mode 100644 index c9b69dd57..000000000 --- a/builder/azure/chroot/step_verify_source_disk_test.go +++ /dev/null @@ -1,157 +0,0 @@ -package chroot - -import ( - "context" - "io/ioutil" - "net/http" - "reflect" - "regexp" - "strings" - "testing" - - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-12-01/compute" - "github.com/Azure/go-autorest/autorest" - "github.com/hashicorp/packer-plugin-sdk/multistep" - packersdk "github.com/hashicorp/packer-plugin-sdk/packer" - "github.com/hashicorp/packer/builder/azure/common/client" -) - -func Test_StepVerifySourceDisk_Run(t *testing.T) { - type fields struct { - SourceDiskResourceID string - Location string - - GetDiskResponseCode int - GetDiskResponseBody string - } - type args struct { - state multistep.StateBag - } - tests := []struct { - name string - fields fields - args args - want multistep.StepAction - errormatch string - }{ - { - name: "HappyPath", - fields: fields{ - SourceDiskResourceID: "/subscriptions/subid1/resourcegroups/rg1/providers/Microsoft.Compute/disks/disk1", - Location: "westus2", - - GetDiskResponseCode: 200, - GetDiskResponseBody: `{"location":"westus2"}`, - }, - want: multistep.ActionContinue, - }, - { - name: "NotAResourceID", - fields: fields{ - SourceDiskResourceID: "/other", - Location: "westus2", - - GetDiskResponseCode: 200, - GetDiskResponseBody: `{"location":"westus2"}`, - }, - want: multistep.ActionHalt, - errormatch: "Could not parse resource id", - }, - { - name: "DiskNotFound", - fields: fields{ - SourceDiskResourceID: "/subscriptions/subid1/resourcegroups/rg1/providers/Microsoft.Compute/disks/disk1", - Location: "westus2", - - GetDiskResponseCode: 404, - GetDiskResponseBody: `{}`, - }, - want: multistep.ActionHalt, - errormatch: "Unable to retrieve", - }, - { - name: "NotADisk", - fields: fields{ - SourceDiskResourceID: "/subscriptions/subid1/resourcegroups/rg1/providers/Microsoft.Compute/images/image1", - Location: "westus2", - - GetDiskResponseCode: 404, - }, - want: multistep.ActionHalt, - errormatch: "not a managed disk", - }, - { - name: "OtherSubscription", - fields: fields{ - SourceDiskResourceID: "/subscriptions/subid2/resourcegroups/rg1/providers/Microsoft.Compute/disks/disk1", - Location: "westus2", - - GetDiskResponseCode: 200, - GetDiskResponseBody: `{"location":"westus2"}`, - }, - want: multistep.ActionHalt, - errormatch: "different subscription", - }, - { - name: "OtherLocation", - fields: fields{ - SourceDiskResourceID: "/subscriptions/subid1/resourcegroups/rg1/providers/Microsoft.Compute/disks/disk1", - Location: "eastus", - - GetDiskResponseCode: 200, - GetDiskResponseBody: `{"location":"westus2"}`, - }, - want: multistep.ActionHalt, - errormatch: "different location", - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - s := StepVerifySourceDisk{ - SourceDiskResourceID: tt.fields.SourceDiskResourceID, - Location: tt.fields.Location, - } - - m := compute.NewDisksClient("subscriptionId") - m.Sender = autorest.SenderFunc(func(r *http.Request) (*http.Response, error) { - return &http.Response{ - Request: r, - Body: ioutil.NopCloser(strings.NewReader(tt.fields.GetDiskResponseBody)), - StatusCode: tt.fields.GetDiskResponseCode, - }, nil - }) - - ui, getErr := testUI() - - state := new(multistep.BasicStateBag) - state.Put("azureclient", &client.AzureClientSetMock{ - DisksClientMock: m, - SubscriptionIDMock: "subid1", - }) - state.Put("ui", ui) - - got := s.Run(context.TODO(), state) - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("StepVerifySourceDisk.Run() = %v, want %v", got, tt.want) - } - - if tt.errormatch != "" { - errs := getErr() - if !regexp.MustCompile(tt.errormatch).MatchString(errs) { - t.Errorf("Expected the error output (%q) to match %q", errs, tt.errormatch) - } - } - - if got == multistep.ActionHalt { - if _, ok := state.GetOk("error"); !ok { - t.Fatal("Expected 'error' to be set in statebag after failure") - } - } - }) - } -} - -type uiThatRemebersErrors struct { - packersdk.Ui - LastError string -} diff --git a/builder/azure/chroot/template_funcs.go b/builder/azure/chroot/template_funcs.go deleted file mode 100644 index 7594cf949..000000000 --- a/builder/azure/chroot/template_funcs.go +++ /dev/null @@ -1,37 +0,0 @@ -package chroot - -import ( - "fmt" - "sync" - - "github.com/hashicorp/packer/builder/azure/common/client" -) - -// CreateVMMetadataTemplateFunc returns a template function that retrieves VM metadata. VM metadata is retrieved only once and reused for all executions of the function. -func CreateVMMetadataTemplateFunc() func(string) (string, error) { - var data *client.ComputeInfo - var dataErr error - once := sync.Once{} - return func(key string) (string, error) { - once.Do(func() { - data, dataErr = client.DefaultMetadataClient.GetComputeInfo() - }) - if dataErr != nil { - return "", dataErr - } - switch key { - case "name": - return data.Name, nil - case "subscription_id": - return data.SubscriptionID, nil - case "resource_group": - return data.ResourceGroupName, nil - case "location": - return data.Location, nil - case "resource_id": - return data.ResourceID(), nil - default: - return "", fmt.Errorf("unknown metadata key: %s (supported: name, subscription_id, resource_group, location, resource_id)", key) - } - } -} diff --git a/builder/azure/common/artifact.go b/builder/azure/common/artifact.go deleted file mode 100644 index 498f7292e..000000000 --- a/builder/azure/common/artifact.go +++ /dev/null @@ -1,104 +0,0 @@ -package common - -import ( - "context" - "fmt" - "log" - "sort" - "strings" - - "github.com/Azure/go-autorest/autorest/azure" - packersdk "github.com/hashicorp/packer-plugin-sdk/packer" - "github.com/hashicorp/packer/builder/azure/common/client" -) - -// Artifact is an artifact implementation that contains built Managed Images or Disks. -type Artifact struct { - // Array of the Azure resource IDs that were created. - Resources []string - - // BuilderId is the unique ID for the builder that created this AMI - BuilderIdValue string - - // Azure client for performing API stuff. - AzureClientSet client.AzureClientSet - - // StateData should store data such as GeneratedData - // to be shared with post-processors - StateData map[string]interface{} -} - -func (a *Artifact) BuilderId() string { - return a.BuilderIdValue -} - -func (*Artifact) Files() []string { - // We have no files - return nil -} - -func (a *Artifact) Id() string { - parts := make([]string, 0, len(a.Resources)) - for _, resource := range a.Resources { - parts = append(parts, strings.ToLower(resource)) - } - - sort.Strings(parts) - return strings.Join(parts, ",") -} - -func (a *Artifact) String() string { - parts := make([]string, 0, len(a.Resources)) - for _, resource := range a.Resources { - parts = append(parts, strings.ToLower(resource)) - } - - sort.Strings(parts) - return fmt.Sprintf("Azure resources created:\n%s\n", strings.Join(parts, "\n")) -} - -func (a *Artifact) State(name string) interface{} { - return a.StateData[name] -} - -func (a *Artifact) Destroy() error { - errs := make([]error, 0) - - for _, resource := range a.Resources { - log.Printf("Deleting resource %s", resource) - - id, err := azure.ParseResourceID(resource) - if err != nil { - return fmt.Errorf("Unable to parse resource id (%s): %v", resource, err) - } - - ctx := context.TODO() - restype := strings.ToLower(fmt.Sprintf("%s/%s", id.Provider, id.ResourceType)) - - switch restype { - case "microsoft.compute/images": - res, err := a.AzureClientSet.ImagesClient().Delete(ctx, id.ResourceGroup, id.ResourceName) - if err != nil { - errs = append(errs, fmt.Errorf("Unable to initiate deletion of resource (%s): %v", resource, err)) - } else { - err := res.WaitForCompletionRef(ctx, a.AzureClientSet.PollClient()) - if err != nil { - errs = append(errs, fmt.Errorf("Unable to complete deletion of resource (%s): %v", resource, err)) - } - } - default: - errs = append(errs, fmt.Errorf("Don't know how to delete resources of type %s (%s)", resource, restype)) - } - - } - - if len(errs) > 0 { - if len(errs) == 1 { - return errs[0] - } else { - return &packersdk.MultiError{Errors: errs} - } - } - - return nil -} diff --git a/builder/azure/common/artifact_test.go b/builder/azure/common/artifact_test.go deleted file mode 100644 index a4fe439c6..000000000 --- a/builder/azure/common/artifact_test.go +++ /dev/null @@ -1,21 +0,0 @@ -package common - -import ( - "testing" -) - -func TestArtifact_String(t *testing.T) { - a := &Artifact{ - Resources: []string{ - "/subscriptions/4674464f-6024-43ae-903c-f6eed761be04/resourceGroups/rg/providers/Microsoft.Compute/disks/PackerTemp-osdisk-1586461959", - "/subscriptions/4674464f-6024-43ae-903c-f6eed761be04/resourceGroups/images/providers/Microsoft.Compute/galleries/testgallery/images/myUbuntu/versions/1.0.10", - }, - } - want := `Azure resources created: -/subscriptions/4674464f-6024-43ae-903c-f6eed761be04/resourcegroups/images/providers/microsoft.compute/galleries/testgallery/images/myubuntu/versions/1.0.10 -/subscriptions/4674464f-6024-43ae-903c-f6eed761be04/resourcegroups/rg/providers/microsoft.compute/disks/packertemp-osdisk-1586461959 -` - if got := a.String(); got != want { - t.Errorf("Artifact.String() = %v, want %v", got, want) - } -} diff --git a/builder/azure/common/client/azure_client_set.go b/builder/azure/common/client/azure_client_set.go deleted file mode 100644 index e1dbbe507..000000000 --- a/builder/azure/common/client/azure_client_set.go +++ /dev/null @@ -1,138 +0,0 @@ -package client - -import ( - "log" - "net/http" - "regexp" - "time" - - "github.com/hashicorp/packer-plugin-sdk/useragent" - - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-12-01/compute" - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-12-01/compute/computeapi" - "github.com/Azure/go-autorest/autorest" - version "github.com/hashicorp/packer/builder/azure/version" -) - -type AzureClientSet interface { - MetadataClient() MetadataClientAPI - - DisksClient() computeapi.DisksClientAPI - SnapshotsClient() computeapi.SnapshotsClientAPI - ImagesClient() computeapi.ImagesClientAPI - - GalleryImagesClient() computeapi.GalleryImagesClientAPI - GalleryImageVersionsClient() computeapi.GalleryImageVersionsClientAPI - - VirtualMachinesClient() computeapi.VirtualMachinesClientAPI - VirtualMachineImagesClient() VirtualMachineImagesClientAPI - - PollClient() autorest.Client - - // SubscriptionID returns the subscription ID that this client set was created for - SubscriptionID() string -} - -var subscriptionPathRegex = regexp.MustCompile(`/subscriptions/([[:xdigit:]]{8}(-[[:xdigit:]]{4}){3}-[[:xdigit:]]{12})`) - -var _ AzureClientSet = &azureClientSet{} - -type azureClientSet struct { - sender autorest.Sender - authorizer autorest.Authorizer - subscriptionID string - PollingDelay time.Duration -} - -func New(c Config, say func(string)) (AzureClientSet, error) { - return new(c, say) -} - -func new(c Config, say func(string)) (*azureClientSet, error) { - token, err := c.GetServicePrincipalToken(say, c.CloudEnvironment().ResourceManagerEndpoint) - if err != nil { - return nil, err - } - return &azureClientSet{ - authorizer: autorest.NewBearerAuthorizer(token), - subscriptionID: c.SubscriptionID, - sender: http.DefaultClient, - PollingDelay: time.Second, - }, nil -} - -func (s azureClientSet) SubscriptionID() string { - return s.subscriptionID -} - -func (s azureClientSet) configureAutorestClient(c *autorest.Client) { - err := c.AddToUserAgent(useragent.String(version.AzurePluginVersion.FormattedVersion())) - if err != nil { - log.Printf("Error appending Packer plugin version to user agent.") - } - c.Authorizer = s.authorizer - c.Sender = s.sender -} - -func (s azureClientSet) MetadataClient() MetadataClientAPI { - return metadataClient{ - s.sender, - useragent.String(version.AzurePluginVersion.FormattedVersion()), - } -} - -func (s azureClientSet) DisksClient() computeapi.DisksClientAPI { - c := compute.NewDisksClient(s.subscriptionID) - s.configureAutorestClient(&c.Client) - c.PollingDelay = s.PollingDelay - return c -} - -func (s azureClientSet) SnapshotsClient() computeapi.SnapshotsClientAPI { - c := compute.NewSnapshotsClient(s.subscriptionID) - s.configureAutorestClient(&c.Client) - c.PollingDelay = s.PollingDelay - return c -} - -func (s azureClientSet) ImagesClient() computeapi.ImagesClientAPI { - c := compute.NewImagesClient(s.subscriptionID) - s.configureAutorestClient(&c.Client) - c.PollingDelay = s.PollingDelay - return c -} - -func (s azureClientSet) VirtualMachinesClient() computeapi.VirtualMachinesClientAPI { - c := compute.NewVirtualMachinesClient(s.subscriptionID) - s.configureAutorestClient(&c.Client) - c.PollingDelay = s.PollingDelay - return c -} - -func (s azureClientSet) VirtualMachineImagesClient() VirtualMachineImagesClientAPI { - c := compute.NewVirtualMachineImagesClient(s.subscriptionID) - s.configureAutorestClient(&c.Client) - c.PollingDelay = s.PollingDelay - return VirtualMachineImagesClient{c} -} - -func (s azureClientSet) GalleryImagesClient() computeapi.GalleryImagesClientAPI { - c := compute.NewGalleryImagesClient(s.subscriptionID) - s.configureAutorestClient(&c.Client) - c.PollingDelay = s.PollingDelay - return c -} - -func (s azureClientSet) GalleryImageVersionsClient() computeapi.GalleryImageVersionsClientAPI { - c := compute.NewGalleryImageVersionsClient(s.subscriptionID) - s.configureAutorestClient(&c.Client) - c.PollingDelay = s.PollingDelay - return c -} - -func (s azureClientSet) PollClient() autorest.Client { - c := autorest.NewClientWithUserAgent("Packer-Azure-ClientSet") - s.configureAutorestClient(&c) - c.PollingDelay = time.Second * 5 - return c -} diff --git a/builder/azure/common/client/azure_client_set_mock.go b/builder/azure/common/client/azure_client_set_mock.go deleted file mode 100644 index b13b79ed6..000000000 --- a/builder/azure/common/client/azure_client_set_mock.go +++ /dev/null @@ -1,72 +0,0 @@ -package client - -import ( - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-12-01/compute/computeapi" - "github.com/Azure/go-autorest/autorest" -) - -var _ AzureClientSet = &AzureClientSetMock{} - -// AzureClientSetMock provides a generic mock for AzureClientSet -type AzureClientSetMock struct { - DisksClientMock computeapi.DisksClientAPI - SnapshotsClientMock computeapi.SnapshotsClientAPI - ImagesClientMock computeapi.ImagesClientAPI - VirtualMachineImagesClientMock VirtualMachineImagesClientAPI - VirtualMachinesClientMock computeapi.VirtualMachinesClientAPI - GalleryImagesClientMock computeapi.GalleryImagesClientAPI - GalleryImageVersionsClientMock computeapi.GalleryImageVersionsClientAPI - PollClientMock autorest.Client - MetadataClientMock MetadataClientAPI - SubscriptionIDMock string -} - -// DisksClient returns a DisksClientAPI -func (m *AzureClientSetMock) DisksClient() computeapi.DisksClientAPI { - return m.DisksClientMock -} - -// SnapshotsClient returns a SnapshotsClientAPI -func (m *AzureClientSetMock) SnapshotsClient() computeapi.SnapshotsClientAPI { - return m.SnapshotsClientMock -} - -// ImagesClient returns a ImagesClientAPI -func (m *AzureClientSetMock) ImagesClient() computeapi.ImagesClientAPI { - return m.ImagesClientMock -} - -// VirtualMachineImagesClient returns a VirtualMachineImagesClientAPI -func (m *AzureClientSetMock) VirtualMachineImagesClient() VirtualMachineImagesClientAPI { - return m.VirtualMachineImagesClientMock -} - -// VirtualMachinesClient returns a VirtualMachinesClientAPI -func (m *AzureClientSetMock) VirtualMachinesClient() computeapi.VirtualMachinesClientAPI { - return m.VirtualMachinesClientMock -} - -// GalleryImagesClient returns a GalleryImagesClientAPI -func (m *AzureClientSetMock) GalleryImagesClient() computeapi.GalleryImagesClientAPI { - return m.GalleryImagesClientMock -} - -// GalleryImageVersionsClient returns a GalleryImageVersionsClientAPI -func (m *AzureClientSetMock) GalleryImageVersionsClient() computeapi.GalleryImageVersionsClientAPI { - return m.GalleryImageVersionsClientMock -} - -// PollClient returns an autorest Client that can be used for polling async requests -func (m *AzureClientSetMock) PollClient() autorest.Client { - return m.PollClientMock -} - -// MetadataClient returns a MetadataClientAPI -func (m *AzureClientSetMock) MetadataClient() MetadataClientAPI { - return m.MetadataClientMock -} - -// SubscriptionID returns SubscriptionIDMock -func (m *AzureClientSetMock) SubscriptionID() string { - return m.SubscriptionIDMock -} diff --git a/builder/azure/common/client/config.go b/builder/azure/common/client/config.go deleted file mode 100644 index 8fa077476..000000000 --- a/builder/azure/common/client/config.go +++ /dev/null @@ -1,352 +0,0 @@ -//go:generate packer-sdc struct-markdown - -package client - -import ( - "fmt" - "os" - "strings" - "time" - - "github.com/Azure/go-autorest/autorest/adal" - "github.com/Azure/go-autorest/autorest/azure" - jwt "github.com/dgrijalva/jwt-go" - packersdk "github.com/hashicorp/packer-plugin-sdk/packer" -) - -// Config allows for various ways to authenticate Azure clients. -// When `client_id` and `subscription_id` are specified, Packer will use the -// specified Azure Active Directory (AAD) Service Principal (SP). -// If only `subscription_id` is specified, Packer will try to interactively -// log on the current user (tokens will be cached). -// If none of these options are specified, Packer will attempt to use the -// Managed Identity and subscription of the VM that Packer is running on. -// This will only work if Packer is running on an Azure VM. -type Config struct { - // One of Public, China, Germany, or - // USGovernment. Defaults to Public. Long forms such as - // USGovernmentCloud and AzureUSGovernmentCloud are also supported. - CloudEnvironmentName string `mapstructure:"cloud_environment_name" required:"false"` - cloudEnvironment *azure.Environment - - // Authentication fields - - // The application ID of the AAD Service Principal. - // Requires either `client_secret`, `client_cert_path` or `client_jwt` to be set as well. - ClientID string `mapstructure:"client_id"` - // A password/secret registered for the AAD SP. - ClientSecret string `mapstructure:"client_secret"` - // The path to a pem-encoded certificate that will be used to authenticate - // as the specified AAD SP. - ClientCertPath string `mapstructure:"client_cert_path"` - // The timeout for the JWT Token when using a [client certificate](#client_cert_path). Defaults to 1 hour. - ClientCertExpireTimeout time.Duration `mapstructure:"client_cert_token_timeout" required:"false"` - // A JWT bearer token for client auth (RFC 7523, Sec. 2.2) that will be used - // to authenticate the AAD SP. Provides more control over token the expiration - // when using certificate authentication than when using `client_cert_path`. - ClientJWT string `mapstructure:"client_jwt"` - // The object ID for the AAD SP. Optional, will be derived from the oAuth token if left empty. - ObjectID string `mapstructure:"object_id"` - - // The Active Directory tenant identifier with which your `client_id` and - // `subscription_id` are associated. If not specified, `tenant_id` will be - // looked up using `subscription_id`. - TenantID string `mapstructure:"tenant_id" required:"false"` - // The subscription to use. - SubscriptionID string `mapstructure:"subscription_id"` - - authType string - - // Flag to use Azure CLI authentication. Defaults to false. - // CLI auth will use the information from an active `az login` session to connect to Azure and set the subscription id and tenant id associated to the signed in account. - // If enabled, it will use the authentication provided by the `az` CLI. - // Azure CLI authentication will use the credential marked as `isDefault` and can be verified using `az account show`. - // Works with normal authentication (`az login`) and service principals (`az login --service-principal --username APP_ID --password PASSWORD --tenant TENANT_ID`). - // Ignores all other configurations if enabled. - UseAzureCLIAuth bool `mapstructure:"use_azure_cli_auth" required:"false"` -} - -const ( - authTypeDeviceLogin = "DeviceLogin" - authTypeMSI = "ManagedIdentity" - authTypeClientSecret = "ClientSecret" - authTypeClientCert = "ClientCertificate" - authTypeClientBearerJWT = "ClientBearerJWT" - authTypeAzureCLI = "AzureCLI" -) - -const DefaultCloudEnvironmentName = "Public" - -func (c *Config) SetDefaultValues() error { - if c.CloudEnvironmentName == "" { - c.CloudEnvironmentName = DefaultCloudEnvironmentName - } - return c.setCloudEnvironment() -} - -func (c *Config) CloudEnvironment() *azure.Environment { - return c.cloudEnvironment -} - -func (c *Config) setCloudEnvironment() error { - lookup := map[string]string{ - "CHINA": "AzureChinaCloud", - "CHINACLOUD": "AzureChinaCloud", - "AZURECHINACLOUD": "AzureChinaCloud", - - "GERMAN": "AzureGermanCloud", - "GERMANCLOUD": "AzureGermanCloud", - "AZUREGERMANCLOUD": "AzureGermanCloud", - - "GERMANY": "AzureGermanCloud", - "GERMANYCLOUD": "AzureGermanCloud", - "AZUREGERMANYCLOUD": "AzureGermanCloud", - - "PUBLIC": "AzurePublicCloud", - "PUBLICCLOUD": "AzurePublicCloud", - "AZUREPUBLICCLOUD": "AzurePublicCloud", - - "USGOVERNMENT": "AzureUSGovernmentCloud", - "USGOVERNMENTCLOUD": "AzureUSGovernmentCloud", - "AZUREUSGOVERNMENTCLOUD": "AzureUSGovernmentCloud", - } - - name := strings.ToUpper(c.CloudEnvironmentName) - envName, ok := lookup[name] - if !ok { - return fmt.Errorf("There is no cloud environment matching the name '%s'!", c.CloudEnvironmentName) - } - - env, err := azure.EnvironmentFromName(envName) - c.cloudEnvironment = &env - return err -} - -//nolint:ineffassign //this triggers a false positive because errs is passed by reference -func (c Config) Validate(errs *packersdk.MultiError) { - ///////////////////////////////////////////// - // Authentication via OAUTH - - // Check if device login is being asked for, and is allowed. - // - // Device login is enabled if the user only defines SubscriptionID and not - // ClientID, ClientSecret, and TenantID. - // - // Device login is not enabled for Windows because the WinRM certificate is - // readable by the ObjectID of the App. There may be another way to handle - // this case, but I am not currently aware of it - send feedback. - - if c.UseCLI() { - return - } - - if c.UseMSI() { - return - } - - if c.useDeviceLogin() { - return - } - - if c.SubscriptionID != "" && c.ClientID != "" && - c.ClientSecret != "" && - c.ClientCertPath == "" && - c.ClientJWT == "" { - // Service principal using secret - return - } - - if c.SubscriptionID != "" && c.ClientID != "" && - c.ClientSecret == "" && - c.ClientCertPath != "" && - c.ClientJWT == "" { - // Service principal using certificate - - if _, err := os.Stat(c.ClientCertPath); err != nil { - errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("client_cert_path is not an accessible file: %v", err)) - } - if c.ClientCertExpireTimeout != 0 && c.ClientCertExpireTimeout < 5*time.Minute { - errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("client_cert_token_timeout will expire within 5 minutes, please set a value greater than 5 minutes")) - } - return - } - - if c.SubscriptionID != "" && c.ClientID != "" && - c.ClientSecret == "" && - c.ClientCertPath == "" && - c.ClientJWT != "" { - // Service principal using JWT - // Check that JWT is valid for at least 5 more minutes - - p := jwt.Parser{} - claims := jwt.StandardClaims{} - token, _, err := p.ParseUnverified(c.ClientJWT, &claims) - if err != nil { - errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("client_jwt is not a JWT: %v", err)) - } else { - if claims.ExpiresAt < time.Now().Add(5*time.Minute).Unix() { - errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("client_jwt will expire within 5 minutes, please use a JWT that is valid for at least 5 minutes")) - } - if t, ok := token.Header["x5t"]; !ok || t == "" { - errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("client_jwt is missing the x5t header value, which is required for bearer JWT client authentication to Azure")) - } - } - - return - } - - errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("No valid set of authentication values specified:\n"+ - " to use the Managed Identity of the current machine, do not specify any of the fields below\n"+ - " to use interactive user authentication, specify only subscription_id\n"+ - " to use an Azure Active Directory service principal, specify either:\n"+ - " - subscription_id, client_id and client_secret\n"+ - " - subscription_id, client_id and client_cert_path\n"+ - " - subscription_id, client_id and client_jwt.")) -} - -func (c Config) useDeviceLogin() bool { - return c.SubscriptionID != "" && - c.ClientID == "" && - c.ClientSecret == "" && - c.ClientJWT == "" && - c.ClientCertPath == "" -} - -func (c Config) UseCLI() bool { - return c.UseAzureCLIAuth == true -} - -func (c Config) UseMSI() bool { - return c.SubscriptionID == "" && - c.ClientID == "" && - c.ClientSecret == "" && - c.ClientJWT == "" && - c.ClientCertPath == "" && - c.TenantID == "" -} - -func (c Config) GetServicePrincipalTokens(say func(string)) ( - servicePrincipalToken *adal.ServicePrincipalToken, - servicePrincipalTokenVault *adal.ServicePrincipalToken, - err error) { - - servicePrincipalToken, err = c.GetServicePrincipalToken(say, - c.CloudEnvironment().ResourceManagerEndpoint) - if err != nil { - return nil, nil, err - } - servicePrincipalTokenVault, err = c.GetServicePrincipalToken(say, - strings.TrimRight(c.CloudEnvironment().KeyVaultEndpoint, "/")) - if err != nil { - return nil, nil, err - } - return servicePrincipalToken, servicePrincipalTokenVault, nil -} - -func (c Config) GetServicePrincipalToken( - say func(string), forResource string) ( - servicePrincipalToken *adal.ServicePrincipalToken, - err error) { - - var auth oAuthTokenProvider - switch c.authType { - case authTypeDeviceLogin: - say("Getting tokens using device flow") - auth = NewDeviceFlowOAuthTokenProvider(*c.cloudEnvironment, say, c.TenantID) - case authTypeAzureCLI: - say("Getting tokens using Azure CLI") - auth = NewCliOAuthTokenProvider(*c.cloudEnvironment, say, c.TenantID) - case authTypeMSI: - say("Getting tokens using Managed Identity for Azure") - auth = NewMSIOAuthTokenProvider(*c.cloudEnvironment) - case authTypeClientSecret: - say("Getting tokens using client secret") - auth = NewSecretOAuthTokenProvider(*c.cloudEnvironment, c.ClientID, c.ClientSecret, c.TenantID) - case authTypeClientCert: - say("Getting tokens using client certificate") - auth, err = NewCertOAuthTokenProvider(*c.cloudEnvironment, c.ClientID, c.ClientCertPath, c.TenantID, c.ClientCertExpireTimeout) - if err != nil { - return nil, err - } - case authTypeClientBearerJWT: - say("Getting tokens using client bearer JWT") - auth = NewJWTOAuthTokenProvider(*c.cloudEnvironment, c.ClientID, c.ClientJWT, c.TenantID) - default: - panic("authType not set, call FillParameters, or set explicitly") - } - - servicePrincipalToken, err = auth.getServicePrincipalTokenWithResource(forResource) - if err != nil { - return nil, err - } - - err = servicePrincipalToken.EnsureFresh() - if err != nil { - return nil, err - } - - return servicePrincipalToken, nil -} - -// FillParameters capture the user intent from the supplied parameter set in authType, retrieves the TenantID and CloudEnvironment if not specified. -// The SubscriptionID is also retrieved in case MSI auth is requested. -func (c *Config) FillParameters() error { - if c.authType == "" { - if c.useDeviceLogin() { - c.authType = authTypeDeviceLogin - } else if c.UseCLI() { - c.authType = authTypeAzureCLI - } else if c.UseMSI() { - c.authType = authTypeMSI - } else if c.ClientSecret != "" { - c.authType = authTypeClientSecret - } else if c.ClientCertPath != "" { - c.authType = authTypeClientCert - } else { - c.authType = authTypeClientBearerJWT - } - } - - if c.authType == authTypeMSI && c.SubscriptionID == "" { - - subscriptionID, err := getSubscriptionFromIMDS() - if err != nil { - return fmt.Errorf("error fetching subscriptionID from VM metadata service for Managed Identity authentication: %v", err) - } - c.SubscriptionID = subscriptionID - } - - if c.cloudEnvironment == nil { - err := c.setCloudEnvironment() - if err != nil { - return err - } - } - - if c.authType == authTypeAzureCLI { - tenantID, subscriptionID, err := getIDsFromAzureCLI() - if err != nil { - return fmt.Errorf("error fetching tenantID and subscriptionID from Azure CLI (are you logged on using `az login`?): %v", err) - } - - c.TenantID = tenantID - c.SubscriptionID = subscriptionID - } - - if c.TenantID == "" { - tenantID, err := findTenantID(*c.cloudEnvironment, c.SubscriptionID) - if err != nil { - return err - } - c.TenantID = tenantID - } - - if c.ClientCertExpireTimeout == 0 { - c.ClientCertExpireTimeout = time.Hour - } - - return nil -} - -// allow override for unit tests -var findTenantID = FindTenantID diff --git a/builder/azure/common/client/config_retriever.go b/builder/azure/common/client/config_retriever.go deleted file mode 100644 index b4b25e5fb..000000000 --- a/builder/azure/common/client/config_retriever.go +++ /dev/null @@ -1,37 +0,0 @@ -package client - -import ( - "encoding/json" - "io/ioutil" - "net/http" -) - -// allow override for unit tests -var getSubscriptionFromIMDS = _getSubscriptionFromIMDS - -func _getSubscriptionFromIMDS() (string, error) { - client := &http.Client{} - - req, _ := http.NewRequest("GET", "http://169.254.169.254/metadata/instance/compute", nil) - req.Header.Add("Metadata", "True") - - q := req.URL.Query() - q.Add("format", "json") - q.Add("api-version", "2017-08-01") - - req.URL.RawQuery = q.Encode() - resp, err := client.Do(req) - if err != nil { - return "", err - } - - defer resp.Body.Close() - resp_body, _ := ioutil.ReadAll(resp.Body) - result := map[string]string{} - err = json.Unmarshal(resp_body, &result) - if err != nil { - return "", err - } - - return result["subscriptionId"], nil -} diff --git a/builder/azure/common/client/config_retriever_test.go b/builder/azure/common/client/config_retriever_test.go deleted file mode 100644 index 6c707e95b..000000000 --- a/builder/azure/common/client/config_retriever_test.go +++ /dev/null @@ -1,56 +0,0 @@ -package client - -import ( - "errors" - "testing" - - "github.com/Azure/go-autorest/autorest/azure" -) - -func TestConfigRetrieverFillsTenantIDWhenEmpty(t *testing.T) { - c := Config{CloudEnvironmentName: "AzurePublicCloud"} - if expected := ""; c.TenantID != expected { - t.Errorf("Expected TenantID to be %q but got %q", expected, c.TenantID) - } - - retrievedTid := "my-tenant-id" - findTenantID = func(azure.Environment, string) (string, error) { return retrievedTid, nil } - getSubscriptionFromIMDS = func() (string, error) { return "unittest", nil } - if err := c.FillParameters(); err != nil { - t.Errorf("Unexpected error when calling c.FillParameters: %v", err) - } - - if expected := retrievedTid; c.TenantID != expected { - t.Errorf("Expected TenantID to be %q but got %q", expected, c.TenantID) - } -} - -func TestConfigRetrieverLeavesTenantIDWhenNotEmpty(t *testing.T) { - c := Config{CloudEnvironmentName: "AzurePublicCloud"} - userSpecifiedTid := "not-empty" - c.TenantID = userSpecifiedTid - - findTenantID = nil // assert that this not even called - getSubscriptionFromIMDS = func() (string, error) { return "unittest", nil } - if err := c.FillParameters(); err != nil { - t.Errorf("Unexpected error when calling c.FillParameters: %v", err) - } - - if expected := userSpecifiedTid; c.TenantID != expected { - t.Errorf("Expected TenantID to be %q but got %q", expected, c.TenantID) - } -} - -func TestConfigRetrieverReturnsErrorWhenTenantIDEmptyAndRetrievalFails(t *testing.T) { - c := Config{CloudEnvironmentName: "AzurePublicCloud"} - if expected := ""; c.TenantID != expected { - t.Errorf("Expected TenantID to be %q but got %q", expected, c.TenantID) - } - - errorString := "sorry, I failed" - findTenantID = func(azure.Environment, string) (string, error) { return "", errors.New(errorString) } - getSubscriptionFromIMDS = func() (string, error) { return "unittest", nil } - if err := c.FillParameters(); err != nil && err.Error() != errorString { - t.Errorf("Unexpected error when calling c.FillParameters: %v", err) - } -} diff --git a/builder/azure/common/client/config_test.go b/builder/azure/common/client/config_test.go deleted file mode 100644 index c2bc05c5b..000000000 --- a/builder/azure/common/client/config_test.go +++ /dev/null @@ -1,444 +0,0 @@ -package client - -import ( - crand "crypto/rand" - "crypto/rsa" - "encoding/base64" - "encoding/binary" - "fmt" - "io" - mrand "math/rand" - "os" - "testing" - "time" - - "github.com/Azure/go-autorest/autorest/azure" - jwt "github.com/dgrijalva/jwt-go" - packersdk "github.com/hashicorp/packer-plugin-sdk/packer" -) - -func Test_ClientConfig_RequiredParametersSet(t *testing.T) { - - tests := []struct { - name string - config Config - wantErr bool - }{ - { - name: "no client_id, client_secret or subscription_id should enable MSI auth", - config: Config{}, - wantErr: false, - }, - { - name: "use_azure_cli_auth will trigger Azure CLI auth", - config: Config{ - UseAzureCLIAuth: true, - }, - wantErr: false, - }, - { - name: "subscription_id is set will trigger device flow", - config: Config{ - SubscriptionID: "error", - }, - wantErr: false, - }, - { - name: "client_id without client_secret, client_cert_path or client_jwt should error", - config: Config{ - ClientID: "error", - }, - wantErr: true, - }, - { - name: "client_secret without client_id should error", - config: Config{ - ClientSecret: "error", - }, - wantErr: true, - }, - { - name: "client_cert_path without client_id should error", - config: Config{ - ClientCertPath: "/dev/null", - }, - wantErr: true, - }, - { - name: "client_jwt without client_id should error", - config: Config{ - ClientJWT: "error", - }, - wantErr: true, - }, - { - name: "missing subscription_id when using secret", - config: Config{ - ClientID: "ok", - ClientSecret: "ok", - }, - wantErr: true, - }, - { - name: "missing subscription_id when using certificate", - config: Config{ - ClientID: "ok", - ClientCertPath: "ok", - }, - wantErr: true, - }, - { - name: "missing subscription_id when using JWT", - config: Config{ - ClientID: "ok", - ClientJWT: "ok", - }, - wantErr: true, - }, - { - name: "client_cert_token_timeout should be 5 minutes or more", - config: Config{ - SubscriptionID: "ok", - ClientID: "ok", - ClientCertPath: "/dev/null", - ClientCertExpireTimeout: 1 * time.Minute, - }, - wantErr: true, - }, - { - name: "too many client_* values", - config: Config{ - SubscriptionID: "ok", - ClientID: "ok", - ClientSecret: "ok", - ClientCertPath: "error", - }, - wantErr: true, - }, - { - name: "too many client_* values (2)", - config: Config{ - SubscriptionID: "ok", - ClientID: "ok", - ClientSecret: "ok", - ClientJWT: "error", - }, - wantErr: true, - }, - { - name: "tenant_id alone should fail", - config: Config{ - TenantID: "ok", - }, - wantErr: true, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - - errs := &packersdk.MultiError{} - tt.config.Validate(errs) - if (len(errs.Errors) != 0) != tt.wantErr { - t.Errorf("newConfig() error = %v, wantErr %v", errs, tt.wantErr) - return - } - }) - } -} - -func Test_ClientConfig_DeviceLogin(t *testing.T) { - getEnvOrSkip(t, "AZURE_DEVICE_LOGIN") - cfg := Config{ - SubscriptionID: getEnvOrSkip(t, "AZURE_SUBSCRIPTION"), - cloudEnvironment: getCloud(), - } - assertValid(t, cfg) - - spt, sptkv, err := cfg.GetServicePrincipalTokens( - func(s string) { fmt.Printf("SAY: %s\n", s) }) - if err != nil { - t.Fatalf("Expected nil err, but got: %v", err) - } - token := spt.Token() - if token.AccessToken == "" { - t.Fatal("Expected management token to have non-nil access token") - } - if token.RefreshToken == "" { - t.Fatal("Expected management token to have non-nil refresh token") - } - kvtoken := sptkv.Token() - if kvtoken.AccessToken == "" { - t.Fatal("Expected keyvault token to have non-nil access token") - } - if kvtoken.RefreshToken == "" { - t.Fatal("Expected keyvault token to have non-nil refresh token") - } -} - -func Test_ClientConfig_AzureCli(t *testing.T) { - // Azure CLI tests skipped unless env 'AZURE_CLI_AUTH' is set, and an active `az login` session has been established - getEnvOrSkip(t, "AZURE_CLI_AUTH") - - cfg := Config{ - UseAzureCLIAuth: true, - cloudEnvironment: getCloud(), - } - assertValid(t, cfg) - - err := cfg.FillParameters() - if err != nil { - t.Fatalf("Expected nil err, but got: %v", err) - } - - if cfg.authType != authTypeAzureCLI { - t.Fatalf("Expected authType to be %q, but got: %q", authTypeAzureCLI, cfg.authType) - } -} - -func Test_ClientConfig_ClientPassword(t *testing.T) { - cfg := Config{ - SubscriptionID: getEnvOrSkip(t, "AZURE_SUBSCRIPTION"), - ClientID: getEnvOrSkip(t, "AZURE_CLIENTID"), - ClientSecret: getEnvOrSkip(t, "AZURE_CLIENTSECRET"), - TenantID: getEnvOrSkip(t, "AZURE_TENANTID"), - cloudEnvironment: getCloud(), - } - assertValid(t, cfg) - - spt, sptkv, err := cfg.GetServicePrincipalTokens(func(s string) { fmt.Printf("SAY: %s\n", s) }) - if err != nil { - t.Fatalf("Expected nil err, but got: %v", err) - } - token := spt.Token() - if token.AccessToken == "" { - t.Fatal("Expected management token to have non-nil access token") - } - if token.RefreshToken != "" { - t.Fatal("Expected management token to have no refresh token") - } - kvtoken := sptkv.Token() - if kvtoken.AccessToken == "" { - t.Fatal("Expected keyvault token to have non-nil access token") - } - if kvtoken.RefreshToken != "" { - t.Fatal("Expected keyvault token to have no refresh token") - } -} - -func Test_ClientConfig_ClientCert(t *testing.T) { - cfg := Config{ - SubscriptionID: getEnvOrSkip(t, "AZURE_SUBSCRIPTION"), - ClientID: getEnvOrSkip(t, "AZURE_CLIENTID"), - ClientCertPath: getEnvOrSkip(t, "AZURE_CLIENTCERT"), - TenantID: getEnvOrSkip(t, "AZURE_TENANTID"), - cloudEnvironment: getCloud(), - } - assertValid(t, cfg) - - spt, sptkv, err := cfg.GetServicePrincipalTokens(func(s string) { fmt.Printf("SAY: %s\n", s) }) - if err != nil { - t.Fatalf("Expected nil err, but got: %v", err) - } - token := spt.Token() - if token.AccessToken == "" { - t.Fatal("Expected management token to have non-nil access token") - } - if token.RefreshToken != "" { - t.Fatal("Expected management token to have no refresh token") - } - kvtoken := sptkv.Token() - if kvtoken.AccessToken == "" { - t.Fatal("Expected keyvault token to have non-nil access token") - } - if kvtoken.RefreshToken != "" { - t.Fatal("Expected keyvault token to have no refresh token") - } -} - -func Test_ClientConfig_ClientJWT(t *testing.T) { - cfg := Config{ - SubscriptionID: getEnvOrSkip(t, "AZURE_SUBSCRIPTION"), - ClientID: getEnvOrSkip(t, "AZURE_CLIENTID"), - ClientJWT: getEnvOrSkip(t, "AZURE_CLIENTJWT"), - TenantID: getEnvOrSkip(t, "AZURE_TENANTID"), - cloudEnvironment: getCloud(), - } - assertValid(t, cfg) - - spt, sptkv, err := cfg.GetServicePrincipalTokens(func(s string) { fmt.Printf("SAY: %s\n", s) }) - if err != nil { - t.Fatalf("Expected nil err, but got: %v", err) - } - token := spt.Token() - if token.AccessToken == "" { - t.Fatal("Expected management token to have non-nil access token") - } - if token.RefreshToken != "" { - t.Fatal("Expected management token to have no refresh token") - } - kvtoken := sptkv.Token() - if kvtoken.AccessToken == "" { - t.Fatal("Expected keyvault token to have non-nil access token") - } - if kvtoken.RefreshToken != "" { - t.Fatal("Expected keyvault token to have no refresh token") - } -} - -func getEnvOrSkip(t *testing.T, envVar string) string { - v := os.Getenv(envVar) - if v == "" { - t.Skipf("%s is empty, skipping", envVar) - } - return v -} - -func getCloud() *azure.Environment { - cloudName := os.Getenv("AZURE_CLOUD") - if cloudName == "" { - cloudName = "AZUREPUBLICCLOUD" - } - c, _ := azure.EnvironmentFromName(cloudName) - return &c -} - -// tests for assertRequiredParametersSet - -func Test_ClientConfig_CanUseDeviceCode(t *testing.T) { - // TenantID is optional, but Builder will look up tenant ID before requesting - t.Run("without TenantID", func(t *testing.T) { - cfg := Config{ - SubscriptionID: "12345", - } - assertValid(t, cfg) - }) - t.Run("with TenantID", func(t *testing.T) { - cfg := Config{ - SubscriptionID: "12345", - TenantID: "12345", - } - assertValid(t, cfg) - }) -} - -func assertValid(t *testing.T, cfg Config) { - errs := &packersdk.MultiError{} - cfg.Validate(errs) - if len(errs.Errors) != 0 { - t.Fatal("Expected errs to be empty: ", errs) - } -} - -func assertInvalid(t *testing.T, cfg Config) { - errs := &packersdk.MultiError{} - cfg.Validate(errs) - if len(errs.Errors) == 0 { - t.Fatal("Expected errs to be non-empty") - } -} - -func Test_ClientConfig_CanUseClientSecret(t *testing.T) { - cfg := Config{ - SubscriptionID: "12345", - ClientID: "12345", - ClientSecret: "12345", - } - - assertValid(t, cfg) -} - -func Test_ClientConfig_CanUseClientSecretWithTenantID(t *testing.T) { - cfg := Config{ - SubscriptionID: "12345", - ClientID: "12345", - ClientSecret: "12345", - TenantID: "12345", - } - - assertValid(t, cfg) -} - -func Test_ClientConfig_CanUseClientJWT(t *testing.T) { - cfg := Config{ - SubscriptionID: "12345", - ClientID: "12345", - ClientJWT: getJWT(10*time.Minute, true), - } - - assertValid(t, cfg) -} - -func Test_ClientConfig_CanUseClientJWTWithTenantID(t *testing.T) { - cfg := Config{ - SubscriptionID: "12345", - ClientID: "12345", - ClientJWT: getJWT(10*time.Minute, true), - TenantID: "12345", - } - - assertValid(t, cfg) -} - -func Test_ClientConfig_CannotUseBothClientJWTAndSecret(t *testing.T) { - cfg := Config{ - SubscriptionID: "12345", - ClientID: "12345", - ClientSecret: "12345", - ClientJWT: getJWT(10*time.Minute, true), - } - - assertInvalid(t, cfg) -} - -func Test_ClientConfig_ClientJWTShouldBeValidForAtLeast5Minutes(t *testing.T) { - cfg := Config{ - SubscriptionID: "12345", - ClientID: "12345", - ClientJWT: getJWT(time.Minute, true), - } - - assertInvalid(t, cfg) -} - -func Test_ClientConfig_ClientJWTShouldHaveThumbprint(t *testing.T) { - cfg := Config{ - SubscriptionID: "12345", - ClientID: "12345", - ClientJWT: getJWT(10*time.Minute, false), - } - - assertInvalid(t, cfg) -} - -func Test_getJWT(t *testing.T) { - if getJWT(time.Minute, true) == "" { - t.Fatalf("getJWT is broken") - } -} - -func newRandReader() io.Reader { - var seed int64 - binary.Read(crand.Reader, binary.LittleEndian, &seed) - - return mrand.New(mrand.NewSource(seed)) -} - -func getJWT(validFor time.Duration, withX5tHeader bool) string { - token := jwt.New(jwt.SigningMethodRS256) - key, _ := rsa.GenerateKey(newRandReader(), 2048) - - token.Claims = jwt.MapClaims{ - "aud": "https://login.microsoftonline.com/tenant.onmicrosoft.com/oauth2/token?api-version=1.0", - "iss": "355dff10-cd78-11e8-89fe-000d3afd16e3", - "sub": "355dff10-cd78-11e8-89fe-000d3afd16e3", - "jti": base64.URLEncoding.EncodeToString([]byte{0}), - "nbf": time.Now().Unix(), - "exp": time.Now().Add(validFor).Unix(), - } - if withX5tHeader { - token.Header["x5t"] = base64.URLEncoding.EncodeToString([]byte("thumbprint")) - } - - jwt, _ := token.SignedString(key) - return jwt -} diff --git a/builder/azure/common/client/detect_azure.go b/builder/azure/common/client/detect_azure.go deleted file mode 100644 index fef7a2932..000000000 --- a/builder/azure/common/client/detect_azure.go +++ /dev/null @@ -1,8 +0,0 @@ -// +build !linux - -package client - -// IsAzure returns true if Packer is running on Azure (currently only works on Linux) -func IsAzure() bool { - return false -} diff --git a/builder/azure/common/client/detect_azure_linux.go b/builder/azure/common/client/detect_azure_linux.go deleted file mode 100644 index c2b9a2800..000000000 --- a/builder/azure/common/client/detect_azure_linux.go +++ /dev/null @@ -1,23 +0,0 @@ -package client - -import ( - "bytes" - "io/ioutil" -) - -var ( - smbiosAssetTagFile = "/sys/class/dmi/id/chassis_asset_tag" - azureAssetTag = []byte("7783-7084-3265-9085-8269-3286-77\n") -) - -// IsAzure returns true if Packer is running on Azure -func IsAzure() bool { - return isAzureAssetTag(smbiosAssetTagFile) -} - -func isAzureAssetTag(filename string) bool { - if d, err := ioutil.ReadFile(filename); err == nil { - return bytes.Compare(d, azureAssetTag) == 0 - } - return false -} diff --git a/builder/azure/common/client/detect_azure_linux_test.go b/builder/azure/common/client/detect_azure_linux_test.go deleted file mode 100644 index 5975d7a6b..000000000 --- a/builder/azure/common/client/detect_azure_linux_test.go +++ /dev/null @@ -1,29 +0,0 @@ -package client - -import ( - "io/ioutil" - "os" - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestIsAzure(t *testing.T) { - f, err := ioutil.TempFile("", "TestIsAzure*") - if err != nil { - t.Fatal(err) - } - defer os.Remove(f.Name()) - - f.Seek(0, 0) - f.Truncate(0) - f.Write([]byte("not the azure assettag")) - - assert.False(t, isAzureAssetTag(f.Name()), "asset tag is not Azure's") - - f.Seek(0, 0) - f.Truncate(0) - f.Write(azureAssetTag) - - assert.True(t, isAzureAssetTag(f.Name()), "asset tag is Azure's") -} diff --git a/builder/azure/common/client/devicelogin.go b/builder/azure/common/client/devicelogin.go deleted file mode 100644 index 0833b5587..000000000 --- a/builder/azure/common/client/devicelogin.go +++ /dev/null @@ -1,208 +0,0 @@ -package client - -import ( - "context" - "fmt" - "net/http" - "os" - "os/user" - "path/filepath" - "regexp" - "strings" - - "github.com/Azure/azure-sdk-for-go/services/resources/mgmt/2016-06-01/subscriptions" - "github.com/Azure/go-autorest/autorest" - "github.com/Azure/go-autorest/autorest/adal" - "github.com/Azure/go-autorest/autorest/azure" - "github.com/Azure/go-autorest/autorest/to" - "github.com/hashicorp/packer-plugin-sdk/useragent" - version "github.com/hashicorp/packer/builder/azure/version" -) - -var ( - // AD app id for packer-azure driver. - clientIDs = map[string]string{ - azure.PublicCloud.Name: "04cc58ec-51ab-4833-ac0d-ce3a7912414b", - azure.USGovernmentCloud.Name: "a1479822-da77-46a7-abd0-6edacc8a8fac", - } -) - -// NOTE(ahmetalpbalkan): Azure Active Directory implements OAuth 2.0 Device Flow -// described here: https://tools.ietf.org/html/draft-denniss-oauth-device-flow-00 -// Although it has some gotchas, most of the authentication logic is in Azure SDK -// for Go helper packages. -// -// Device auth prints a message to the screen telling the user to click on URL -// and approve the app on the browser, meanwhile the client polls the auth API -// for a token. Once we have token, we save it locally to a file with proper -// permissions and when the token expires (in Azure case typically 1 hour) SDK -// will automatically refresh the specified token and will call the refresh -// callback function we implement here. This way we will always be storing a -// token with a refresh_token saved on the machine. - -// Authenticate fetches a token from the local file cache or initiates a consent -// flow and waits for token to be obtained. -func Authenticate(env azure.Environment, tenantID string, say func(string), scope string) (*adal.ServicePrincipalToken, error) { - clientID, ok := clientIDs[env.Name] - var resourceid string - - if !ok { - return nil, fmt.Errorf("packer-azure application not set up for Azure environment %q", env.Name) - } - - oauthCfg, err := adal.NewOAuthConfig(env.ActiveDirectoryEndpoint, tenantID) - if err != nil { - return nil, fmt.Errorf("Failed to obtain oauth config for azure environment: %v", err) - } - - // for AzurePublicCloud (https://management.core.windows.net/), this old - // Service Management scope covers both ASM and ARM. - - if strings.Contains(scope, "vault") { - resourceid = "vault" - } else { - resourceid = "mgmt" - } - - tokenPath := tokenCachePath(tenantID + resourceid) - saveToken := mkTokenCallback(tokenPath) - saveTokenCallback := func(t adal.Token) error { - say("Azure token expired. Saving the refreshed token...") - return saveToken(t) - } - - // Lookup the token cache file for an existing token. - spt, err := tokenFromFile(say, *oauthCfg, tokenPath, clientID, scope, saveTokenCallback) - if err != nil { - return nil, err - } - if spt != nil { - say(fmt.Sprintf("Auth token found in file: %s", tokenPath)) - return spt, nil - } - - // Start an OAuth 2.0 device flow - say(fmt.Sprintf("Initiating device flow: %s", tokenPath)) - spt, err = tokenFromDeviceFlow(say, *oauthCfg, clientID, scope) - if err != nil { - return nil, err - } - say("Obtained service principal token.") - if err := saveToken(spt.Token()); err != nil { - say("Error occurred saving token to cache file.") - return nil, err - } - return spt, nil -} - -// tokenFromFile returns a token from the specified file if it is found, otherwise -// returns nil. Any error retrieving or creating the token is returned as an error. -func tokenFromFile(say func(string), oauthCfg adal.OAuthConfig, tokenPath, clientID, resource string, - callback adal.TokenRefreshCallback) (*adal.ServicePrincipalToken, error) { - say(fmt.Sprintf("Loading auth token from file: %s", tokenPath)) - if _, err := os.Stat(tokenPath); err != nil { - if os.IsNotExist(err) { // file not found - return nil, nil - } - return nil, err - } - - token, err := adal.LoadToken(tokenPath) - if err != nil { - return nil, fmt.Errorf("Failed to load token from file: %v", err) - } - - spt, err := adal.NewServicePrincipalTokenFromManualToken(oauthCfg, clientID, resource, *token, callback) - if err != nil { - return nil, fmt.Errorf("Error constructing service principal token: %v", err) - } - return spt, nil -} - -// tokenFromDeviceFlow prints a message to the screen for user to take action to -// consent application on a browser and in the meanwhile the authentication -// endpoint is polled until user gives consent, denies or the flow times out. -// Returned token must be saved. -func tokenFromDeviceFlow(say func(string), oauthCfg adal.OAuthConfig, clientID, resource string) (*adal.ServicePrincipalToken, error) { - cl := autorest.NewClientWithUserAgent(useragent.String(version.AzurePluginVersion.FormattedVersion())) - deviceCode, err := adal.InitiateDeviceAuth(&cl, oauthCfg, clientID, resource) - if err != nil { - return nil, fmt.Errorf("Failed to start device auth: %v", err) - } - - // Example message: “To sign in, open https://aka.ms/devicelogin and enter - // the code 0000000 to authenticate.” - say(fmt.Sprintf("Microsoft Azure: %s", to.String(deviceCode.Message))) - - token, err := adal.WaitForUserCompletion(&cl, deviceCode) - if err != nil { - return nil, fmt.Errorf("Failed to complete device auth: %v", err) - } - - spt, err := adal.NewServicePrincipalTokenFromManualToken(oauthCfg, clientID, resource, *token) - if err != nil { - return nil, fmt.Errorf("Error constructing service principal token: %v", err) - } - return spt, nil -} - -// tokenCachePath returns the full path the OAuth 2.0 token should be saved at -// for given tenant ID. -func tokenCachePath(tenantID string) string { - var dir string - - u, err := user.Current() - if err != nil || u.HomeDir == "" { - dir, _ = filepath.Abs(os.Args[0]) - } else { - dir = u.HomeDir - } - - return filepath.Join(dir, ".azure", "packer", fmt.Sprintf("oauth-%s.json", tenantID)) -} - -// mkTokenCallback returns a callback function that can be used to save the -// token initially or register to the Azure SDK to be called when the token is -// refreshed. -func mkTokenCallback(path string) adal.TokenRefreshCallback { - return func(t adal.Token) error { - if err := adal.SaveToken(path, 0600, t); err != nil { - return err - } - return nil - } -} - -// FindTenantID figures out the AAD tenant ID of the subscription by making an -// unauthenticated request to the Get Subscription Details endpoint and parses -// the value from WWW-Authenticate header. -func FindTenantID(env azure.Environment, subscriptionID string) (string, error) { - const hdrKey = "WWW-Authenticate" - c := subscriptions.NewClientWithBaseURI(env.ResourceManagerEndpoint) - - // we expect this request to fail (err != nil), but we are only interested - // in headers, so surface the error if the Response is not present (i.e. - // network error etc) - subs, err := c.Get(context.TODO(), subscriptionID) - if subs.Response.Response == nil { - return "", fmt.Errorf("Request failed: %v", err) - } - - // Expecting 401 StatusUnauthorized here, just read the header - if subs.StatusCode != http.StatusUnauthorized { - return "", fmt.Errorf("Unexpected response from Get Subscription: %v", err) - } - hdr := subs.Header.Get(hdrKey) - if hdr == "" { - return "", fmt.Errorf("Header %v not found in Get Subscription response", hdrKey) - } - - // Example value for hdr: - // Bearer authorization_uri="https://login.windows.net/996fe9d1-6171-40aa-945b-4c64b63bf655", error="invalid_token", error_description="The authentication failed because of missing 'Authorization' header." - r := regexp.MustCompile(`authorization_uri=".*/([0-9a-f\-]+)"`) - m := r.FindStringSubmatch(hdr) - if m == nil { - return "", fmt.Errorf("Could not find the tenant ID in header: %s %q", hdrKey, hdr) - } - return m[1], nil -} diff --git a/builder/azure/common/client/metadata.go b/builder/azure/common/client/metadata.go deleted file mode 100644 index da8bbef45..000000000 --- a/builder/azure/common/client/metadata.go +++ /dev/null @@ -1,94 +0,0 @@ -package client - -import ( - "fmt" - "net/http" - "time" - - "github.com/Azure/go-autorest/autorest" - "github.com/Azure/go-autorest/autorest/azure" -) - -// DefaultMetadataClient is the default instance metadata client for Azure. Replace this variable for testing purposes only -var DefaultMetadataClient = NewMetadataClient() - -// MetadataClientAPI holds methods that Packer uses to get information about the current VM -type MetadataClientAPI interface { - GetComputeInfo() (*ComputeInfo, error) -} - -// MetadataClientStub is an easy way to put a test hook in DefaultMetadataClient -type MetadataClientStub struct { - ComputeInfo -} - -//GetComputeInfo implements MetadataClientAPI -func (s MetadataClientStub) GetComputeInfo() (*ComputeInfo, error) { - return &s.ComputeInfo, nil -} - -// ComputeInfo defines the Azure VM metadata that is used in Packer -type ComputeInfo struct { - Name string - ResourceGroupName string - SubscriptionID string - Location string -} - -// metadataClient implements MetadataClient -type metadataClient struct { - autorest.Sender - UserAgent string -} - -var _ MetadataClientAPI = metadataClient{} - -const imdsURL = "http://169.254.169.254/metadata/instance?api-version=2017-08-01" - -// VMResourceID returns the resource ID of the current VM -func (client metadataClient) GetComputeInfo() (*ComputeInfo, error) { - req, err := autorest.CreatePreparer( - autorest.AsGet(), - autorest.WithHeader("Metadata", "true"), - autorest.WithUserAgent(client.UserAgent), - autorest.WithBaseURL(imdsURL), - ).Prepare((&http.Request{})) - if err != nil { - return nil, err - } - - res, err := autorest.SendWithSender(client, req, - autorest.DoRetryForDuration(1*time.Minute, 5*time.Second)) - if err != nil { - return nil, err - } - - var vminfo struct { - ComputeInfo `json:"compute"` - } - - err = autorest.Respond( - res, - azure.WithErrorUnlessStatusCode(http.StatusOK), - autorest.ByUnmarshallingJSON(&vminfo), - autorest.ByClosing()) - if err != nil { - return nil, err - } - return &vminfo.ComputeInfo, nil -} - -func (ci ComputeInfo) ResourceID() string { - return fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Compute/virtualMachines/%s", - ci.SubscriptionID, - ci.ResourceGroupName, - ci.Name, - ) -} - -// NewMetadataClient creates a new instance metadata client -func NewMetadataClient() MetadataClientAPI { - return metadataClient{ - Sender: autorest.CreateSender(), - } -} diff --git a/builder/azure/common/client/metadata_test.go b/builder/azure/common/client/metadata_test.go deleted file mode 100644 index 75353d141..000000000 --- a/builder/azure/common/client/metadata_test.go +++ /dev/null @@ -1,32 +0,0 @@ -package client - -import ( - "fmt" - "testing" - - "github.com/Azure/go-autorest/autorest/azure" - - "github.com/stretchr/testify/assert" -) - -func Test_MetadataReturnsComputeInfo(t *testing.T) { - if !IsAzure() { - t.Skipf("Not running on Azure, skipping live IMDS test") - } - mdc := NewMetadataClient() - info, err := mdc.GetComputeInfo() - assert.Nil(t, err) - - vm, err := azure.ParseResourceID(fmt.Sprintf( - "/subscriptions/%s"+ - "/resourceGroups/%s"+ - "/providers/Microsoft.Compute"+ - "/virtualMachines/%s", - info.SubscriptionID, - info.ResourceGroupName, - info.Name)) - assert.Nil(t, err, "%q is not parsable as an Azure resource info", info) - - assert.Regexp(t, "^[0-9a-fA-F-]{36}$", vm.SubscriptionID) - t.Logf("VM: %+v", vm) -} diff --git a/builder/azure/common/client/normalize_location.go b/builder/azure/common/client/normalize_location.go deleted file mode 100644 index 6a82d73f1..000000000 --- a/builder/azure/common/client/normalize_location.go +++ /dev/null @@ -1,9 +0,0 @@ -package client - -import "strings" - -// NormalizeLocation returns a normalized location string. -// Strings are converted to lower case and spaces are removed. -func NormalizeLocation(loc string) string { - return strings.ReplaceAll(strings.ToLower(loc), " ", "") -} diff --git a/builder/azure/common/client/normalize_location_test.go b/builder/azure/common/client/normalize_location_test.go deleted file mode 100644 index adec5aa04..000000000 --- a/builder/azure/common/client/normalize_location_test.go +++ /dev/null @@ -1,22 +0,0 @@ -package client - -import "testing" - -func TestNormalizeLocation(t *testing.T) { - tests := []struct { - name string - loc string - want string - }{ - {"removes spaces", " with spaces ", "withspaces"}, - {"makes lowercase", "MiXed Case", "mixedcase"}, - {"North East US", "North East US", "northeastus"}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if got := NormalizeLocation(tt.loc); got != tt.want { - t.Errorf("NormalizeLocation() = %v, want %v", got, tt.want) - } - }) - } -} diff --git a/builder/azure/common/client/platform_image.go b/builder/azure/common/client/platform_image.go deleted file mode 100644 index d2e798def..000000000 --- a/builder/azure/common/client/platform_image.go +++ /dev/null @@ -1,58 +0,0 @@ -package client - -import ( - "context" - "fmt" - "regexp" - "strings" - - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-12-01/compute" - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-12-01/compute/computeapi" - "github.com/Azure/go-autorest/autorest/to" -) - -var platformImageRegex = regexp.MustCompile(`^[-_.a-zA-Z0-9]+:[-_.a-zA-Z0-9]+:[-_.a-zA-Z0-9]+:[-_.a-zA-Z0-9]+$`) - -type VirtualMachineImagesClientAPI interface { - computeapi.VirtualMachineImagesClientAPI - // extensions - GetLatest(ctx context.Context, publisher, offer, sku, location string) (*compute.VirtualMachineImageResource, error) -} - -var _ VirtualMachineImagesClientAPI = VirtualMachineImagesClient{} - -type VirtualMachineImagesClient struct { - computeapi.VirtualMachineImagesClientAPI -} - -func ParsePlatformImageURN(urn string) (image *PlatformImage, err error) { - if !platformImageRegex.Match([]byte(urn)) { - return nil, fmt.Errorf("%q is not a valid platform image specifier", urn) - } - parts := strings.Split(urn, ":") - return &PlatformImage{parts[0], parts[1], parts[2], parts[3]}, nil -} - -func (c VirtualMachineImagesClient) GetLatest(ctx context.Context, publisher, offer, sku, location string) (*compute.VirtualMachineImageResource, error) { - result, err := c.List(ctx, location, publisher, offer, sku, "", to.Int32Ptr(1), "name desc") - if err != nil { - return nil, err - } - if result.Value == nil || len(*result.Value) == 0 { - return nil, fmt.Errorf("%s:%s:%s:latest could not be found in location %s", publisher, offer, sku, location) - } - - return &(*result.Value)[0], nil -} - -type PlatformImage struct { - Publisher, Offer, Sku, Version string -} - -func (pi PlatformImage) URN() string { - return fmt.Sprintf("%s:%s:%s:%s", - pi.Publisher, - pi.Offer, - pi.Sku, - pi.Version) -} diff --git a/builder/azure/common/client/platform_image_test.go b/builder/azure/common/client/platform_image_test.go deleted file mode 100644 index 981031d45..000000000 --- a/builder/azure/common/client/platform_image_test.go +++ /dev/null @@ -1,30 +0,0 @@ -package client - -import ( - "fmt" - "testing" -) - -func Test_platformImageRegex(t *testing.T) { - for i, v := range []string{ - "Publisher:Offer:Sku:Versions", - "Publisher:Offer-name:2.0_alpha:2.0.2019060122", - } { - t.Run(fmt.Sprintf("should_match_%d", i), func(t *testing.T) { - if !platformImageRegex.Match([]byte(v)) { - t.Fatalf("expected %q to match", v) - } - }) - } - - for i, v := range []string{ - "Publ isher:Offer:Sku:Versions", - "Publ/isher:Offer-name:2.0_alpha:2.0.2019060122", - } { - t.Run(fmt.Sprintf("should_not_match_%d", i), func(t *testing.T) { - if platformImageRegex.Match([]byte(v)) { - t.Fatalf("did not expected %q to match", v) - } - }) - } -} diff --git a/builder/azure/common/client/resource.go b/builder/azure/common/client/resource.go deleted file mode 100644 index db1163fa4..000000000 --- a/builder/azure/common/client/resource.go +++ /dev/null @@ -1,125 +0,0 @@ -package client - -import ( - "errors" - "fmt" - "strings" -) - -// ParseResourceID parses an Azure resource ID -func ParseResourceID(resourceID string) (Resource, error) { - resourceID = strings.Trim(resourceID, "/") - segments := strings.Split(resourceID, "/") - if len(segments)%2 != 0 { - return Resource{}, errors.New("Expected even number of segments") - } - - npairs := len(segments) / 2 - - keys := make([]string, npairs) - values := make([]string, npairs) - for i := 0; i < len(segments); i += 2 { - keys[i/2] = segments[i] - values[i/2] = segments[i+1] - - if keys[i/2] == "" { - return Resource{}, fmt.Errorf("Found empty segment (%d)", i) - } - if values[i/2] == "" { - return Resource{}, fmt.Errorf("Found empty segment (%d)", i+1) - } - } - - if !strings.EqualFold(keys[0], "subscriptions") { - return Resource{}, fmt.Errorf("Expected first segment to be 'subscriptions', but found %q", keys[0]) - } - - if !strings.EqualFold(keys[1], "resourceGroups") { - return Resource{}, fmt.Errorf("Expected second segment to be 'resourceGroups', but found %q", keys[1]) - } - - if !strings.EqualFold(keys[2], "providers") { - return Resource{}, fmt.Errorf("Expected third segment to be 'providers', but found %q", keys[1]) - } - - r := Resource{ - values[0], - values[1], - values[2], - CompoundName(keys[3:]), - CompoundName(values[3:]), - } - if err := r.Validate(); err != nil { - return Resource{}, fmt.Errorf("Error validating resource: %w", err) - } - - return r, nil -} - -type Resource struct { - Subscription string - ResourceGroup string - Provider string - ResourceType CompoundName - ResourceName CompoundName -} - -func (r Resource) String() string { - return fmt.Sprintf( - "/subscriptions/%s"+ - "/resourceGroups/%s"+ - "/providers/%s"+ - "/%s", - r.Subscription, - r.ResourceGroup, - r.Provider, - strings.Join(zipstrings(r.ResourceType, r.ResourceName), "/")) -} - -func (r Resource) Validate() error { - if r.Subscription == "" { - return errors.New("subscription is not set") - } - if r.ResourceGroup == "" { - return errors.New("resource group is not set") - } - if r.Provider == "" { - return errors.New("provider is not set") - } - if len(r.ResourceType) > len(r.ResourceName) { - return errors.New("not enough values in resource name") - } - if len(r.ResourceType) < len(r.ResourceName) { - return errors.New("too many values in resource name") - } - return nil -} - -// Parent produces a resource ID representing the parent resource if this is a child resource -func (r Resource) Parent() (Resource, error) { - newLen := len(r.ResourceType) - 1 - if newLen == 0 { - return Resource{}, errors.New("Top-level resource has no parent") - } - return Resource{ - Subscription: r.Subscription, - ResourceGroup: r.ResourceGroup, - Provider: r.Provider, - ResourceType: r.ResourceType[:newLen], - ResourceName: r.ResourceName[:newLen], - }, nil -} - -type CompoundName []string - -func (n CompoundName) String() string { - return strings.Join(n, "/") -} - -func zipstrings(a []string, b []string) []string { - c := make([]string, 0, len(a)+len(b)) - for i := 0; i < len(a) && i < len(b); i++ { - c = append(c, a[i], b[i]) - } - return c -} diff --git a/builder/azure/common/client/resource_test.go b/builder/azure/common/client/resource_test.go deleted file mode 100644 index 10084ef15..000000000 --- a/builder/azure/common/client/resource_test.go +++ /dev/null @@ -1,188 +0,0 @@ -package client - -import ( - "reflect" - "testing" -) - -func TestParseResourceID(t *testing.T) { - tests := []struct { - name string - resourceID string - want Resource - wantErr bool - }{ - { - name: "happy path", - resourceID: "/subscriptions/17c60680-0e49-465b-aa54-ece043ce5571/resourceGroups/rg/providers/Microsoft.Resources/resources/resource", - want: Resource{ - Subscription: "17c60680-0e49-465b-aa54-ece043ce5571", - ResourceGroup: "rg", - Provider: "Microsoft.Resources", - ResourceType: CompoundName{"resources"}, - ResourceName: CompoundName{"resource"}, - }, - }, - { - name: "sub resource", - resourceID: "/subscriptions/17c60680-0e49-465b-aa54-ece043ce5571/resourcegroups/rg/providers/Microsoft.Resources/resources/resource/subResources/child", - want: Resource{ - Subscription: "17c60680-0e49-465b-aa54-ece043ce5571", - ResourceGroup: "rg", - Provider: "Microsoft.Resources", - ResourceType: CompoundName{"resources", "subResources"}, - ResourceName: CompoundName{"resource", "child"}, - }, - }, - { - name: "incomplete", - resourceID: "/subscriptions/17c60680-0e49-465b-aa54-ece043ce5571/resourceGroups/rg/providers/Microsoft.Resources/resources/resource/subResources", - wantErr: true, - }, - { - name: "incomplete 2", - resourceID: "/subscriptions/17c60680-0e49-465b-aa54-ece043ce5571/resourceGroups/rg/providers/Microsoft.Resources/resources/", - wantErr: true, - }, - { - name: "extra slash", - resourceID: "/subscriptions/17c60680-0e49-465b-aa54-ece043ce5571/resourceGroups/rg/providers/Microsoft.Resources/resources//resources", - wantErr: true, - }, - { - name: "empty resource name", - resourceID: "/subscriptions/17c60680-0e49-465b-aa54-ece043ce5571/resourceGroups/rg/providers/Microsoft.Resources/resources//subresources/child", - wantErr: true, - }, - { - name: "empty sub resource type", - resourceID: "/subscriptions/17c60680-0e49-465b-aa54-ece043ce5571/resourceGroups/rg/providers/Microsoft.Resources/resources/resource//child", - wantErr: true, - }, - { - name: "ungrouped resource path", - resourceID: "/subscriptions/17c60680-0e49-465b-aa54-ece043ce5571/providers/Microsoft.Resources/resources/resource", - wantErr: true, - }, - { - name: "misspelled subscriptions", - resourceID: "/subscription/17c60680-0e49-465b-aa54-ece043ce5571/resourceGroups/rg/providers/Microsoft.Resources/resources/resource/subResources/child", - wantErr: true, - }, - { - name: "misspelled resourceGroups", - resourceID: "/subscriptions/17c60680-0e49-465b-aa54-ece043ce5571/resourceGroup/rg/providers/Microsoft.Resources/resources/resource/subResources/child", - wantErr: true, - }, - { - name: "misspelled providers", - resourceID: "/subscriptions/17c60680-0e49-465b-aa54-ece043ce5571/resourceGroups/rg/provider/Microsoft.Resources/resources/resource/subResources/child", - wantErr: true, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := ParseResourceID(tt.resourceID) - if (err != nil) != tt.wantErr { - t.Errorf("ParseResourceID() error = %v, wantErr %v", err, tt.wantErr) - return - } - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("ParseResourceID() = %v, want %v", got, tt.want) - } - }) - } -} - -func TestResource_String(t *testing.T) { - type fields struct { - Subscription string - ResourceGroup string - Provider string - ResourceType CompoundName - ResourceName CompoundName - } - tests := []struct { - name string - fields fields - want string - }{ - { - name: "happy path", - fields: fields{ - Subscription: "sub", - ResourceGroup: "rg", - Provider: "provider", - ResourceType: CompoundName{"type"}, - ResourceName: CompoundName{"name"}, - }, - want: "/subscriptions/sub/resourceGroups/rg/providers/provider/type/name", - }, - { - name: "happy path - child resource", - fields: fields{ - Subscription: "sub", - ResourceGroup: "rg", - Provider: "provider", - ResourceType: CompoundName{"type", "sub"}, - ResourceName: CompoundName{"name", "child"}, - }, - want: "/subscriptions/sub/resourceGroups/rg/providers/provider/type/name/sub/child", - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - r := Resource{ - Subscription: tt.fields.Subscription, - ResourceGroup: tt.fields.ResourceGroup, - Provider: tt.fields.Provider, - ResourceType: tt.fields.ResourceType, - ResourceName: tt.fields.ResourceName, - } - if got := r.String(); got != tt.want { - t.Errorf("Resource.String() = %v, want %v", got, tt.want) - } - }) - } -} - -func TestResource_Parent(t *testing.T) { - tests := []struct { - name string - resourceID string - want string - wantErr bool - }{ - { - name: "happy path", - resourceID: "/subscriptions/17c60680-0e49-465b-aa54-ece043ce5571/resourceGroups/rg/providers/Microsoft.Resources/resources/resource/sub/child", - want: "/subscriptions/17c60680-0e49-465b-aa54-ece043ce5571/resourceGroups/rg/providers/Microsoft.Resources/resources/resource", - }, - { - name: "sub sub", - resourceID: "/subscriptions/17c60680-0e49-465b-aa54-ece043ce5571/resourceGroups/rg/providers/Microsoft.Resources/resources/resource/sub/child/subsub/grandchild", - want: "/subscriptions/17c60680-0e49-465b-aa54-ece043ce5571/resourceGroups/rg/providers/Microsoft.Resources/resources/resource/sub/child", - }, - { - name: "top level resource", - resourceID: "/subscriptions/17c60680-0e49-465b-aa54-ece043ce5571/resourceGroups/rg/providers/Microsoft.Resources/resources/resource", - wantErr: true, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - r, err := ParseResourceID(tt.resourceID) - if err != nil { - t.Fatalf("Error parsing test resource: %v", err) - } - got, err := r.Parent() - if (err != nil) != tt.wantErr { - t.Errorf("Resource.Parent() error = %v, wantErr %v", err, tt.wantErr) - return - } - if err == nil && got.String() != tt.want { - t.Errorf("Resource.Parent() = %v, want %v", got, tt.want) - } - }) - } -} diff --git a/builder/azure/common/client/testclient.go b/builder/azure/common/client/testclient.go deleted file mode 100644 index afb2156dc..000000000 --- a/builder/azure/common/client/testclient.go +++ /dev/null @@ -1,30 +0,0 @@ -package client - -import ( - "errors" - "net/http" - "os" - "testing" - - "github.com/Azure/go-autorest/autorest/azure/auth" -) - -func GetTestClientSet(t *testing.T) (AzureClientSet, error) { - if os.Getenv("AZURE_INTEGRATION_TEST") == "" { - t.Skip("AZURE_INTEGRATION_TEST not set") - } else { - a, err := auth.NewAuthorizerFromEnvironment() - if err == nil { - cli := azureClientSet{} - cli.authorizer = a - cli.subscriptionID = os.Getenv("AZURE_SUBSCRIPTION_ID") - cli.PollingDelay = 0 - cli.sender = http.DefaultClient - return cli, nil - } else { - t.Skipf("Could not create Azure client: %v", err) - } - } - - return nil, errors.New("Couldn't create client set") -} diff --git a/builder/azure/common/client/tokenprovider.go b/builder/azure/common/client/tokenprovider.go deleted file mode 100644 index 86cd80a9c..000000000 --- a/builder/azure/common/client/tokenprovider.go +++ /dev/null @@ -1,10 +0,0 @@ -package client - -import ( - "github.com/Azure/go-autorest/autorest/adal" -) - -type oAuthTokenProvider interface { - getServicePrincipalToken() (*adal.ServicePrincipalToken, error) - getServicePrincipalTokenWithResource(resource string) (*adal.ServicePrincipalToken, error) -} diff --git a/builder/azure/common/client/tokenprovider_cert.go b/builder/azure/common/client/tokenprovider_cert.go deleted file mode 100644 index 64decad46..000000000 --- a/builder/azure/common/client/tokenprovider_cert.go +++ /dev/null @@ -1,165 +0,0 @@ -package client - -import ( - "crypto/ecdsa" - "crypto/rand" - "crypto/rsa" - "crypto/sha1" - "crypto/x509" - "encoding/base64" - "encoding/pem" - "fmt" - "io/ioutil" - "os" - "time" - - "github.com/Azure/go-autorest/autorest/azure" - "github.com/dgrijalva/jwt-go" - "github.com/hashicorp/packer/builder/azure/pkcs12" -) - -func NewCertOAuthTokenProvider(env azure.Environment, clientID, clientCertPath, tenantID string, certExpireTimeout time.Duration) (oAuthTokenProvider, error) { - cert, key, err := readCert(clientCertPath) - if err != nil { - return nil, fmt.Errorf("Error reading certificate: %v", err) - } - - audience := fmt.Sprintf("%s%s/oauth2/token", env.ActiveDirectoryEndpoint, tenantID) - jwt, err := makeJWT(clientID, audience, cert, key, certExpireTimeout, true) - if err != nil { - return nil, fmt.Errorf("Error generating JWT: %v", err) - } - - return NewJWTOAuthTokenProvider(env, clientID, jwt, tenantID), nil -} - -// Creates a new JSON Web Token to be used as bearer JWT to authenticate -// to the Azure AD token endpoint to retrieve an access token for `audience`. -// If the full certificate is included in the token, then issuer/subject name -// could be used to authenticate if configured by the identity provider (AAD). -func makeJWT(clientID string, audience string, - cert *x509.Certificate, privatekey interface{}, - validFor time.Duration, includeFullCertificate bool) (string, error) { - - // The jti (JWT ID) claim provides a unique identifier for the JWT. - // See https://tools.ietf.org/html/rfc7519#section-4.1.7 - jti := make([]byte, 20) - _, err := rand.Read(jti) - if err != nil { - return "", err - } - - var token *jwt.Token - if cert.PublicKeyAlgorithm == x509.RSA { - token = jwt.New(jwt.SigningMethodRS256) - } else if cert.PublicKeyAlgorithm == x509.ECDSA { - token = jwt.New(jwt.SigningMethodES256) - } else { - return "", fmt.Errorf("Don't know how to handle this type of key algorithm: %v", cert.PublicKeyAlgorithm) - } - - hasher := sha1.New() - if _, err := hasher.Write(cert.Raw); err != nil { - return "", err - } - thumbprint := base64.URLEncoding.EncodeToString(hasher.Sum(nil)) - - // X.509 thumbprint, see https://tools.ietf.org/html/rfc7515#section-4.1.7 - token.Header["x5t"] = thumbprint - if includeFullCertificate { - // X.509 certificate (chain), see https://tools.ietf.org/html/rfc7515#section-4.1.6 - token.Header["x5c"] = []string{base64.StdEncoding.EncodeToString(cert.Raw)} - } - - token.Claims = jwt.MapClaims{ - // See https://tools.ietf.org/html/rfc7519#section-4.1 - "aud": audience, - "iss": clientID, - "sub": clientID, - "jti": base64.URLEncoding.EncodeToString(jti), - "nbf": time.Now().Unix(), - "exp": time.Now().Add(validFor).Unix(), - } - - return token.SignedString(privatekey) -} - -func readCert(file string) (cert *x509.Certificate, key interface{}, err error) { - f, err := os.Open(file) - if err != nil { - return nil, nil, err - } - defer f.Close() - d, err := ioutil.ReadAll(f) - if err != nil { - return nil, nil, err - } - - blocks := []*pem.Block{} - for len(d) > 0 { - var b *pem.Block - b, d = pem.Decode(d) - if b == nil { - break - } - blocks = append(blocks, b) - } - - certs := []*x509.Certificate{} - for _, block := range blocks { - if block.Type == "CERTIFICATE" { - c, err := x509.ParseCertificate(block.Bytes) - if err != nil { - return nil, nil, fmt.Errorf( - "Failed to read certificate block: %v", err) - } - certs = append(certs, c) - } else if block.Type == "PRIVATE KEY" { - key, err = x509.ParsePKCS8PrivateKey(block.Bytes) - if err != nil { - return nil, nil, fmt.Errorf( - "Failed to read private key block: %v", err) - } - } - // Don't care about other types of blocks, ignore - } - - if key == nil { - key, cert, err = pkcs12.Decode(d, "") - if err != nil { - return nil, nil, fmt.Errorf( - "Did not find private key in file, tried to read as PKCS#12 and failed: %v", err) - } - certs = append(certs, cert) - } - - if key == nil { - return nil, nil, fmt.Errorf("Did not find private key in file") - } - - // find the certificate that belongs to the private key by comparing the public keys - switch key := key.(type) { - case *rsa.PrivateKey: - for _, c := range certs { - if cp, ok := c.PublicKey.(*rsa.PublicKey); ok && - (cp.N.Cmp(key.PublicKey.N) == 0) { - cert = c - } - } - - case *ecdsa.PrivateKey: - for _, c := range certs { - if cp, ok := c.PublicKey.(*ecdsa.PublicKey); ok && - (cp.X.Cmp(key.PublicKey.X) == 0) && - (cp.Y.Cmp(key.PublicKey.Y) == 0) { - cert = c - } - } - } - - if cert == nil { - return nil, nil, fmt.Errorf("Did not find certificate belonging to private key in file") - } - - return cert, key, nil -} diff --git a/builder/azure/common/client/tokenprovider_cli.go b/builder/azure/common/client/tokenprovider_cli.go deleted file mode 100644 index b11182e20..000000000 --- a/builder/azure/common/client/tokenprovider_cli.go +++ /dev/null @@ -1,99 +0,0 @@ -package client - -import ( - "context" - "errors" - "fmt" - - "github.com/Azure/go-autorest/autorest/adal" - "github.com/Azure/go-autorest/autorest/azure" - "github.com/Azure/go-autorest/autorest/azure/cli" -) - -// for managed identity auth -type cliOAuthTokenProvider struct { - env azure.Environment - say func(string) - tenantID string -} - -func NewCliOAuthTokenProvider(env azure.Environment, say func(string), tenantID string) oAuthTokenProvider { - return &cliOAuthTokenProvider{ - env: env, - say: say, - tenantID: tenantID, - } -} - -func (tp *cliOAuthTokenProvider) getServicePrincipalToken() (*adal.ServicePrincipalToken, error) { - return tp.getServicePrincipalTokenWithResource(tp.env.ResourceManagerEndpoint) -} - -func (tp *cliOAuthTokenProvider) getServicePrincipalTokenWithResource(resource string) (*adal.ServicePrincipalToken, error) { - token, err := cli.GetTokenFromCLI(resource) - if err != nil { - tp.say(fmt.Sprintf("unable to get token from azure cli: %v", err)) - return nil, err - } - - oAuthConfig, err := adal.NewOAuthConfig(resource, tp.tenantID) - if err != nil { - tp.say(fmt.Sprintf("unable to generate OAuth Config: %v", err)) - return nil, err - } - - adalToken, err := token.ToADALToken() - if err != nil { - tp.say(fmt.Sprintf("unable to get ADAL Token from azure cli token: %v", err)) - return nil, err - } - - spt, err := adal.NewServicePrincipalTokenFromManualToken(*oAuthConfig, clientIDs[tp.env.Name], resource, adalToken) - if err != nil { - tp.say(fmt.Sprintf("unable to get service principal token from adal token: %v", err)) - return nil, err - } - - // Custom refresh function to make it possible to use Azure CLI to refresh tokens. - // Inspired by HashiCorps go-azure-helpers: https://github.com/hashicorp/go-azure-helpers/blob/373622ce2effb0cf299051ea019cb657f357a4d8/authentication/auth_method_azure_cli_token.go#L96-L109 - var customRefreshFunc adal.TokenRefresh = func(ctx context.Context, resource string) (*adal.Token, error) { - token, err := cli.GetTokenFromCLI(resource) - if err != nil { - tp.say(fmt.Sprintf("token refresh - unable to get token from azure cli: %v", err)) - return nil, err - } - - adalToken, err := token.ToADALToken() - if err != nil { - tp.say(fmt.Sprintf("token refresh - unable to get ADAL Token from azure cli token: %v", err)) - return nil, err - } - - return &adalToken, nil - } - - spt.SetCustomRefreshFunc(customRefreshFunc) - - return spt, nil -} - -// getIDsFromAzureCLI returns the TenantID and SubscriptionID from an active Azure CLI login session -func getIDsFromAzureCLI() (string, string, error) { - profilePath, err := cli.ProfilePath() - if err != nil { - return "", "", err - } - - profile, err := cli.LoadProfile(profilePath) - if err != nil { - return "", "", err - } - - for _, p := range profile.Subscriptions { - if p.IsDefault { - return p.TenantID, p.ID, nil - } - } - - return "", "", errors.New("Unable to find default subscription") -} diff --git a/builder/azure/common/client/tokenprovider_devicewflow.go b/builder/azure/common/client/tokenprovider_devicewflow.go deleted file mode 100644 index 00d83488a..000000000 --- a/builder/azure/common/client/tokenprovider_devicewflow.go +++ /dev/null @@ -1,39 +0,0 @@ -package client - -import ( - "fmt" - "strings" - - "github.com/Azure/go-autorest/autorest/adal" - "github.com/Azure/go-autorest/autorest/azure" -) - -func NewDeviceFlowOAuthTokenProvider(env azure.Environment, say func(string), tenantID string) oAuthTokenProvider { - return &deviceflowOauthTokenProvider{ - env: env, - say: say, - tenantID: tenantID, - } -} - -type deviceflowOauthTokenProvider struct { - env azure.Environment - say func(string) - tenantID string -} - -func (tp *deviceflowOauthTokenProvider) getServicePrincipalToken() (*adal.ServicePrincipalToken, error) { - return tp.getServicePrincipalTokenWithResource(tp.env.ResourceManagerEndpoint) -} - -func (tp *deviceflowOauthTokenProvider) getServicePrincipalTokenWithResource(resource string) (*adal.ServicePrincipalToken, error) { - if resource == tp.env.ServiceManagementEndpoint { - tp.say("Getting auth token for Service management endpoint") - } else if resource == strings.TrimRight(tp.env.KeyVaultEndpoint, "/") { - tp.say("Getting token for Vault resource") - } else { - tp.say(fmt.Sprintf("Getting token for %s", resource)) - } - - return Authenticate(tp.env, tp.tenantID, tp.say, resource) -} diff --git a/builder/azure/common/client/tokenprovider_jwt.go b/builder/azure/common/client/tokenprovider_jwt.go deleted file mode 100644 index 905d1f22f..000000000 --- a/builder/azure/common/client/tokenprovider_jwt.go +++ /dev/null @@ -1,43 +0,0 @@ -package client - -import ( - "net/url" - - "github.com/Azure/go-autorest/autorest/adal" - "github.com/Azure/go-autorest/autorest/azure" -) - -// for clientID/bearer JWT auth -type jwtOAuthTokenProvider struct { - env azure.Environment - clientID, clientJWT, tenantID string -} - -func NewJWTOAuthTokenProvider(env azure.Environment, clientID, clientJWT, tenantID string) oAuthTokenProvider { - return &jwtOAuthTokenProvider{env, clientID, clientJWT, tenantID} -} - -func (tp *jwtOAuthTokenProvider) getServicePrincipalToken() (*adal.ServicePrincipalToken, error) { - return tp.getServicePrincipalTokenWithResource(tp.env.ResourceManagerEndpoint) -} - -func (tp *jwtOAuthTokenProvider) getServicePrincipalTokenWithResource(resource string) (*adal.ServicePrincipalToken, error) { - oauthConfig, err := adal.NewOAuthConfig(tp.env.ActiveDirectoryEndpoint, tp.tenantID) - if err != nil { - return nil, err - } - - return adal.NewServicePrincipalTokenWithSecret( - *oauthConfig, - tp.clientID, - resource, - tp) -} - -// implements github.com/Azure/go-autorest/autorest/adal.ServicePrincipalSecret -func (tp *jwtOAuthTokenProvider) SetAuthenticationValues( - t *adal.ServicePrincipalToken, v *url.Values) error { - v.Set("client_assertion", tp.clientJWT) - v.Set("client_assertion_type", "urn:ietf:params:oauth:client-assertion-type:jwt-bearer") - return nil -} diff --git a/builder/azure/common/client/tokenprovider_msi.go b/builder/azure/common/client/tokenprovider_msi.go deleted file mode 100644 index 8c97a821d..000000000 --- a/builder/azure/common/client/tokenprovider_msi.go +++ /dev/null @@ -1,23 +0,0 @@ -package client - -import ( - "github.com/Azure/go-autorest/autorest/adal" - "github.com/Azure/go-autorest/autorest/azure" -) - -// for managed identity auth -type msiOAuthTokenProvider struct { - env azure.Environment -} - -func NewMSIOAuthTokenProvider(env azure.Environment) oAuthTokenProvider { - return &msiOAuthTokenProvider{env} -} - -func (tp *msiOAuthTokenProvider) getServicePrincipalToken() (*adal.ServicePrincipalToken, error) { - return tp.getServicePrincipalTokenWithResource(tp.env.ResourceManagerEndpoint) -} - -func (tp *msiOAuthTokenProvider) getServicePrincipalTokenWithResource(resource string) (*adal.ServicePrincipalToken, error) { - return adal.NewServicePrincipalTokenFromMSI("http://169.254.169.254/metadata/identity/oauth2/token", resource) -} diff --git a/builder/azure/common/client/tokenprovider_secret.go b/builder/azure/common/client/tokenprovider_secret.go deleted file mode 100644 index 7834742bb..000000000 --- a/builder/azure/common/client/tokenprovider_secret.go +++ /dev/null @@ -1,35 +0,0 @@ -package client - -import ( - "github.com/Azure/go-autorest/autorest/adal" - "github.com/Azure/go-autorest/autorest/azure" -) - -// for clientID/secret auth -type secretOAuthTokenProvider struct { - env azure.Environment - clientID, clientSecret, tenantID string -} - -func NewSecretOAuthTokenProvider(env azure.Environment, clientID, clientSecret, tenantID string) oAuthTokenProvider { - return &secretOAuthTokenProvider{env, clientID, clientSecret, tenantID} -} - -func (tp *secretOAuthTokenProvider) getServicePrincipalToken() (*adal.ServicePrincipalToken, error) { - return tp.getServicePrincipalTokenWithResource(tp.env.ResourceManagerEndpoint) -} - -func (tp *secretOAuthTokenProvider) getServicePrincipalTokenWithResource(resource string) (*adal.ServicePrincipalToken, error) { - oauthConfig, err := adal.NewOAuthConfig(tp.env.ActiveDirectoryEndpoint, tp.tenantID) - if err != nil { - return nil, err - } - - spt, err := adal.NewServicePrincipalToken( - *oauthConfig, - tp.clientID, - tp.clientSecret, - resource) - - return spt, err -} diff --git a/builder/azure/common/client/tokenprovider_secret_test.go b/builder/azure/common/client/tokenprovider_secret_test.go deleted file mode 100644 index 9ca685e47..000000000 --- a/builder/azure/common/client/tokenprovider_secret_test.go +++ /dev/null @@ -1,40 +0,0 @@ -package client - -import ( - "testing" - - "github.com/Azure/go-autorest/autorest/azure" -) - -// Behavior is the most important thing to assert for ServicePrincipalToken, but -// that cannot be done in a unit test because it involves network access. Instead, -// I assert the expected inertness of this class. -func TestNewSecretOAuthTokenProvider(t *testing.T) { - testSubject := NewSecretOAuthTokenProvider(azure.PublicCloud, "clientID", "clientString", "tenantID") - spn, err := testSubject.getServicePrincipalToken() - if err != nil { - t.Fatalf(err.Error()) - } - - if spn.Token().AccessToken != "" { - t.Errorf("spn.Token().AccessToken: expected=\"\", actual=%s", spn.Token().AccessToken) - } - if spn.Token().RefreshToken != "" { - t.Errorf("spn.Token().RefreshToken: expected=\"\", actual=%s", spn.Token().RefreshToken) - } - if spn.Token().ExpiresIn != "0" { - t.Errorf("spn.Token().ExpiresIn: expected=\"0\", actual=%s", spn.Token().ExpiresIn) - } - if spn.Token().ExpiresOn != "0" { - t.Errorf("spn.Token().ExpiresOn: expected=\"0\", actual=%s", spn.Token().ExpiresOn) - } - if spn.Token().NotBefore != "0" { - t.Errorf("spn.Token().NotBefore: expected=\"0\", actual=%s", spn.Token().NotBefore) - } - if spn.Token().Resource != "" { - t.Errorf("spn.Token().Resource: expected=\"\", actual=%s", spn.Token().Resource) - } - if spn.Token().Type != "" { - t.Errorf("spn.Token().Type: expected=\"\", actual=%s", spn.Token().Type) - } -} diff --git a/builder/azure/common/constants/stateBag.go b/builder/azure/common/constants/stateBag.go deleted file mode 100644 index 8a8ba1de6..000000000 --- a/builder/azure/common/constants/stateBag.go +++ /dev/null @@ -1,58 +0,0 @@ -package constants - -// complete flags -const ( - AuthorizedKey string = "authorizedKey" - Certificate string = "certificate" - Error string = "error" - SSHHost string = "sshHost" - Thumbprint string = "thumbprint" - Ui string = "ui" -) - -// Default replica count for image versions in shared image gallery -const ( - SharedImageGalleryImageVersionDefaultMinReplicaCount int32 = 1 - SharedImageGalleryImageVersionDefaultMaxReplicaCount int32 = 10 -) - -const ( - ArmCaptureTemplate string = "arm.CaptureTemplate" - ArmComputeName string = "arm.ComputeName" - ArmImageParameters string = "arm.ImageParameters" - ArmCertificateUrl string = "arm.CertificateUrl" - ArmKeyVaultDeploymentName string = "arm.KeyVaultDeploymentName" - ArmDeploymentName string = "arm.DeploymentName" - ArmNicName string = "arm.NicName" - ArmKeyVaultName string = "arm.KeyVaultName" - ArmLocation string = "arm.Location" - ArmOSDiskVhd string = "arm.OSDiskVhd" - ArmAdditionalDiskVhds string = "arm.AdditionalDiskVhds" - ArmPublicIPAddressName string = "arm.PublicIPAddressName" - ArmResourceGroupName string = "arm.ResourceGroupName" - ArmIsResourceGroupCreated string = "arm.IsResourceGroupCreated" - ArmDoubleResourceGroupNameSet string = "arm.DoubleResourceGroupNameSet" - ArmStorageAccountName string = "arm.StorageAccountName" - ArmTags string = "arm.Tags" - ArmVirtualMachineCaptureParameters string = "arm.VirtualMachineCaptureParameters" - ArmIsExistingResourceGroup string = "arm.IsExistingResourceGroup" - ArmIsExistingKeyVault string = "arm.IsExistingKeyVault" - ArmIsManagedImage string = "arm.IsManagedImage" - ArmManagedImageResourceGroupName string = "arm.ManagedImageResourceGroupName" - ArmManagedImageName string = "arm.ManagedImageName" - ArmManagedImageSigPublishResourceGroup string = "arm.ManagedImageSigPublishResourceGroup" - ArmManagedImageSharedGalleryName string = "arm.ManagedImageSharedGalleryName" - ArmManagedImageSharedGalleryImageName string = "arm.ManagedImageSharedGalleryImageName" - ArmManagedImageSharedGalleryImageVersion string = "arm.ManagedImageSharedGalleryImageVersion" - ArmManagedImageSharedGalleryReplicationRegions string = "arm.ManagedImageSharedGalleryReplicationRegions" - ArmManagedImageSharedGalleryId string = "arm.ArmManagedImageSharedGalleryId" - ArmManagedImageSharedGalleryImageVersionEndOfLifeDate string = "arm.ArmManagedImageSharedGalleryImageVersionEndOfLifeDate" - ArmManagedImageSharedGalleryImageVersionReplicaCount string = "arm.ArmManagedImageSharedGalleryImageVersionReplicaCount" - ArmManagedImageSharedGalleryImageVersionExcludeFromLatest string = "arm.ArmManagedImageSharedGalleryImageVersionExcludeFromLatest" - ArmManagedImageSharedGalleryImageVersionStorageAccountType string = "arm.ArmManagedImageSharedGalleryImageVersionStorageAccountType" - ArmManagedImageSubscription string = "arm.ArmManagedImageSubscription" - ArmAsyncResourceGroupDelete string = "arm.AsyncResourceGroupDelete" - ArmManagedImageOSDiskSnapshotName string = "arm.ManagedImageOSDiskSnapshotName" - ArmManagedImageDataDiskSnapshotPrefix string = "arm.ManagedImageDataDiskSnapshotPrefix" - ArmKeepOSDisk string = "arm.KeepOSDisk" -) diff --git a/builder/azure/common/constants/targetplatforms.go b/builder/azure/common/constants/targetplatforms.go deleted file mode 100644 index 515eb7466..000000000 --- a/builder/azure/common/constants/targetplatforms.go +++ /dev/null @@ -1,7 +0,0 @@ -package constants - -// Target types -const ( - Target_Linux string = "Linux" - Target_Windows string = "Windows" -) diff --git a/builder/azure/common/dump_config.go b/builder/azure/common/dump_config.go deleted file mode 100644 index 73e619e76..000000000 --- a/builder/azure/common/dump_config.go +++ /dev/null @@ -1,70 +0,0 @@ -package common - -import ( - "fmt" - "reflect" - "strings" - - "github.com/mitchellh/reflectwalk" -) - -type walker struct { - depth int - say func(string) -} - -func newDumpConfig(say func(string)) *walker { - return &walker{ - depth: 0, - say: say, - } -} - -func (s *walker) Enter(l reflectwalk.Location) error { - s.depth += 1 - return nil -} - -func (s *walker) Exit(l reflectwalk.Location) error { - s.depth -= 1 - return nil -} - -func (s *walker) Struct(v reflect.Value) error { - return nil -} - -func (s *walker) StructField(f reflect.StructField, v reflect.Value) error { - if !s.shouldDump(v) { - return nil - } - - switch v.Kind() { - case reflect.String: - s.say(fmt.Sprintf("%s=%s", f.Name, s.formatValue(f.Name, v.String()))) - } - - return nil -} - -func (s *walker) shouldDump(v reflect.Value) bool { - return s.depth == 2 && v.IsValid() && v.CanInterface() -} - -func (s *walker) formatValue(name, value string) string { - if s.isMaskable(name) { - return strings.Repeat("*", len(value)) - } - - return value -} - -func (s *walker) isMaskable(name string) bool { - up := strings.ToUpper(name) - return strings.Contains(up, "SECRET") || strings.Contains(up, "PASSWORD") -} - -func DumpConfig(config interface{}, say func(string)) { - walker := newDumpConfig(say) - reflectwalk.Walk(config, walker) -} diff --git a/builder/azure/common/dump_config_test.go b/builder/azure/common/dump_config_test.go deleted file mode 100644 index 20f2e488b..000000000 --- a/builder/azure/common/dump_config_test.go +++ /dev/null @@ -1,127 +0,0 @@ -package common - -import "testing" - -func TestShouldDumpPublicValues(t *testing.T) { - type S struct { - MyString string - myString string - } - - data := &S{ - MyString: "value1", - } - - dumps := make([]string, 0, 2) - DumpConfig(data, func(s string) { dumps = append(dumps, s) }) - - if len(dumps) != 1 { - t.Fatalf("Expected len(dumps) to be 1, but got %d", len(dumps)) - - } - if dumps[0] != "MyString=value1" { - t.Errorf("Expected dumps[0] to be 'MyString=value1', but got %s", dumps[0]) - } -} - -func TestShouldOnlyDumpStrings(t *testing.T) { - type S struct { - MyString string - MyInt int - MyFloat32 float32 - MyStringPointer *string - } - - s := "value1" - data := &S{ - MyString: s, - MyInt: 1, - MyFloat32: 2.0, - MyStringPointer: &s, - } - - dumps := make([]string, 0, 4) - DumpConfig(data, func(s string) { dumps = append(dumps, s) }) - - if len(dumps) != 1 { - t.Fatalf("Expected len(dumps) to be 1, but got %d", len(dumps)) - - } - if dumps[0] != "MyString=value1" { - t.Errorf("Expected dumps[0] to be 'MyString=value1', but got %s", dumps[0]) - } -} - -func TestDumpShouldMaskSensitiveFieldValues(t *testing.T) { - type S struct { - MyString string - MySecret string - MySecretValue string - MyPassword string - MyPasswordValue string - } - - data := &S{ - MyString: "my-string", - MySecret: "s3cr3t", - MySecretValue: "s3cr3t-value", - MyPassword: "p@ssw0rd", - MyPasswordValue: "p@ssw0rd-value", - } - - dumps := make([]string, 0, 5) - DumpConfig(data, func(s string) { dumps = append(dumps, s) }) - - if len(dumps) != 5 { - t.Fatalf("Expected len(dumps) to be 5, but got %d", len(dumps)) - - } - if dumps[0] != "MyString=my-string" { - t.Errorf("Expected dumps[0] to be 'MyString=my-string', but got %s", dumps[0]) - } - if dumps[1] != "MySecret=******" { - t.Errorf("Expected dumps[1] to be 'MySecret=******', but got %s", dumps[1]) - } - if dumps[2] != "MySecretValue=************" { - t.Errorf("Expected dumps[2] to be 'MySecret=************', but got %s", dumps[2]) - } - if dumps[3] != "MyPassword=********" { - t.Errorf("Expected dumps[3] to be 'MyPassword=******** but got %s", dumps[3]) - } - if dumps[4] != "MyPasswordValue=**************" { - t.Errorf("Expected dumps[4] to be 'MyPasswordValue=**************' but got %s", dumps[4]) - } -} - -func TestDumpConfigShouldDumpTopLevelValuesOnly(t *testing.T) { - type N struct { - NestedString string - } - - type S struct { - Nested1 N - MyString string - Nested2 N - } - - data := &S{ - Nested1: N{ - NestedString: "nested-string1", - }, - MyString: "my-string", - Nested2: N{ - NestedString: "nested-string2", - }, - } - - dumps := make([]string, 0, 1) - DumpConfig(data, func(s string) { dumps = append(dumps, s) }) - - if len(dumps) != 1 { - t.Fatalf("Expected len(dumps) to be 1, but got %d", len(dumps)) - - } - if dumps[0] != "MyString=my-string" { - t.Errorf("Expected dumps[0] to be 'MyString=my-string', but got %s", dumps[0]) - } -} diff --git a/builder/azure/common/gluestrings.go b/builder/azure/common/gluestrings.go deleted file mode 100644 index e3b6963d3..000000000 --- a/builder/azure/common/gluestrings.go +++ /dev/null @@ -1,19 +0,0 @@ -package common - -// removes overlap between the end of a and the start of b and -// glues them together -func GlueStrings(a, b string) string { - shift := 0 - for shift < len(a) { - i := 0 - for (i+shift < len(a)) && (i < len(b)) && (a[i+shift] == b[i]) { - i++ - } - if i+shift == len(a) { - break - } - shift++ - } - - return a[:shift] + b -} diff --git a/builder/azure/common/gluestrings_test.go b/builder/azure/common/gluestrings_test.go deleted file mode 100644 index 0e7a633d4..000000000 --- a/builder/azure/common/gluestrings_test.go +++ /dev/null @@ -1,30 +0,0 @@ -package common - -import ( - "testing" -) - -func TestGlueStrings(t *testing.T) { - cases := []struct{ a, b, expected string }{ - { - "Some log that starts in a", - "starts in a, but continues in b", - "Some log that starts in a, but continues in b", - }, - { - "", - "starts in b", - "starts in b", - }, - } - for _, testcase := range cases { - t.Logf("testcase: %+v\n", testcase) - - result := GlueStrings(testcase.a, testcase.b) - t.Logf("result: '%s'", result) - - if result != testcase.expected { - t.Errorf("expected %q, got %q", testcase.expected, result) - } - } -} diff --git a/builder/azure/common/lin/ssh.go b/builder/azure/common/lin/ssh.go deleted file mode 100644 index 0b91a41d4..000000000 --- a/builder/azure/common/lin/ssh.go +++ /dev/null @@ -1,11 +0,0 @@ -package lin - -import ( - "github.com/hashicorp/packer-plugin-sdk/multistep" - "github.com/hashicorp/packer/builder/azure/common/constants" -) - -func SSHHost(state multistep.StateBag) (string, error) { - host := state.Get(constants.SSHHost).(string) - return host, nil -} diff --git a/builder/azure/common/logutil/logfields.go b/builder/azure/common/logutil/logfields.go deleted file mode 100644 index b450594de..000000000 --- a/builder/azure/common/logutil/logfields.go +++ /dev/null @@ -1,16 +0,0 @@ -package logutil - -import "fmt" - -type Fields map[string]interface{} - -func (f Fields) String() string { - var s string - for k, v := range f { - if sv, ok := v.(string); ok { - v = fmt.Sprintf("%q", sv) - } - s += fmt.Sprintf(" %s=%v", k, v) - } - return s -} diff --git a/builder/azure/common/map.go b/builder/azure/common/map.go deleted file mode 100644 index 318f2780b..000000000 --- a/builder/azure/common/map.go +++ /dev/null @@ -1,10 +0,0 @@ -package common - -func MapToAzureTags(in map[string]string) map[string]*string { - res := map[string]*string{} - for k := range in { - v := in[k] - res[k] = &v - } - return res -} diff --git a/builder/azure/common/state_bag.go b/builder/azure/common/state_bag.go deleted file mode 100644 index e62969c14..000000000 --- a/builder/azure/common/state_bag.go +++ /dev/null @@ -1,8 +0,0 @@ -package common - -import "github.com/hashicorp/packer-plugin-sdk/multistep" - -func IsStateCancelled(stateBag multistep.StateBag) bool { - _, ok := stateBag.GetOk(multistep.StateCancelled) - return ok -} diff --git a/builder/azure/common/strings_contains.go b/builder/azure/common/strings_contains.go deleted file mode 100644 index 2a8fae416..000000000 --- a/builder/azure/common/strings_contains.go +++ /dev/null @@ -1,13 +0,0 @@ -package common - -import "strings" - -// StringsContains returns true if the `haystack` contains the `needle`. Search is case insensitive. -func StringsContains(haystack []string, needle string) bool { - for _, s := range haystack { - if strings.EqualFold(s, needle) { - return true - } - } - return false -} diff --git a/builder/azure/common/strings_contains_test.go b/builder/azure/common/strings_contains_test.go deleted file mode 100644 index 51a24ff70..000000000 --- a/builder/azure/common/strings_contains_test.go +++ /dev/null @@ -1,39 +0,0 @@ -package common - -import "testing" - -func TestStringsContains(t *testing.T) { - - tests := []struct { - name string - haystack []string - needle string - want bool - }{ - { - name: "found", - haystack: []string{"a", "b", "c"}, - needle: "b", - want: true, - }, - { - name: "missing", - haystack: []string{"a", "b", "c"}, - needle: "D", - want: false, - }, - { - name: "case insensitive", - haystack: []string{"a", "b", "c"}, - needle: "B", - want: true, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if got := StringsContains(tt.haystack, tt.needle); got != tt.want { - t.Errorf("StringsContains() = %v, want %v", got, tt.want) - } - }) - } -} diff --git a/builder/azure/common/template/TestBuildLinux02.approved.txt b/builder/azure/common/template/TestBuildLinux02.approved.txt deleted file mode 100644 index 97aad4de1..000000000 --- a/builder/azure/common/template/TestBuildLinux02.approved.txt +++ /dev/null @@ -1,120 +0,0 @@ -{ - "$schema": "http://schema.management.azure.com/schemas/2014-04-01-preview/deploymentTemplate.json", - "contentVersion": "1.0.0.0", - "parameters": { - "adminPassword": { - "type": "string" - }, - "adminUsername": { - "type": "string" - }, - "dnsNameForPublicIP": { - "type": "string" - }, - "osDiskName": { - "type": "string" - }, - "storageAccountBlobEndpoint": { - "type": "string" - }, - "vmName": { - "type": "string" - }, - "vmSize": { - "type": "string" - } - }, - "variables": { - "addressPrefix": "10.0.0.0/16", - "apiVersion": "2015-06-15", - "location": "[resourceGroup().location]", - "nicName": "packerNic", - "publicIPAddressName": "packerPublicIP", - "publicIPAddressType": "Dynamic", - "sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]", - "subnetAddressPrefix": "10.0.0.0/24", - "subnetName": "--subnet-name--", - "subnetRef": "[concat(variables('vnetID'),'/subnets/',variables('subnetName'))]", - "virtualNetworkName": "--virtual-network--", - "virtualNetworkResourceGroup": "--virtual-network-resource-group--", - "vmStorageAccountContainerName": "images", - "vnetID": "[resourceId(variables('virtualNetworkResourceGroup'), 'Microsoft.Network/virtualNetworks', variables('virtualNetworkName'))]" - }, - "resources": [ - { - "apiVersion": "[variables('apiVersion')]", - "name": "[variables('nicName')]", - "type": "Microsoft.Network/networkInterfaces", - "location": "[variables('location')]", - "dependsOn": [], - "properties": { - "ipConfigurations": [ - { - "properties": { - "privateIPAllocationMethod": "Dynamic", - "subnet": { - "id": "[variables('subnetRef')]" - } - }, - "name": "ipconfig" - } - ] - } - }, - { - "apiVersion": "[variables('apiVersion')]", - "name": "[parameters('vmName')]", - "type": "Microsoft.Compute/virtualMachines", - "location": "[variables('location')]", - "dependsOn": [ - "[concat('Microsoft.Network/networkInterfaces/', variables('nicName'))]" - ], - "properties": { - "diagnosticsProfile": { - "bootDiagnostics": { - "enabled": false - } - }, - "hardwareProfile": { - "vmSize": "[parameters('vmSize')]" - }, - "networkProfile": { - "networkInterfaces": [ - { - "id": "[resourceId('Microsoft.Network/networkInterfaces', variables('nicName'))]" - } - ] - }, - "osProfile": { - "computerName": "[parameters('vmName')]", - "adminUsername": "[parameters('adminUsername')]", - "adminPassword": "[parameters('adminPassword')]", - "linuxConfiguration": { - "ssh": { - "publicKeys": [ - { - "path": "[variables('sshKeyPath')]", - "keyData": "--test-ssh-authorized-key--" - } - ] - } - } - }, - "storageProfile": { - "osDisk": { - "osType": "Linux", - "name": "[parameters('osDiskName')]", - "vhd": { - "uri": "[concat(parameters('storageAccountBlobEndpoint'),variables('vmStorageAccountContainerName'),'/', parameters('osDiskName'),'.vhd')]" - }, - "image": { - "uri": "http://azure/custom.vhd" - }, - "caching": "ReadWrite", - "createOption": "FromImage" - } - } - } - } - ] -} \ No newline at end of file diff --git a/builder/azure/common/template/template.go b/builder/azure/common/template/template.go deleted file mode 100644 index 2366b7a24..000000000 --- a/builder/azure/common/template/template.go +++ /dev/null @@ -1,124 +0,0 @@ -package template - -import ( - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2018-04-01/compute" - "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2018-01-01/network" -) - -///////////////////////////////////////////////// -// Template -type Template struct { - Schema *string `json:"$schema"` - ContentVersion *string `json:"contentVersion"` - Parameters *map[string]Parameters `json:"parameters"` - Variables *map[string]string `json:"variables"` - Resources []*Resource `json:"resources"` -} - -///////////////////////////////////////////////// -// Template > Parameters -type Parameters struct { - Type *string `json:"type"` - DefaultValue *string `json:"defaultValue,omitempty"` -} - -///////////////////////////////////////////////// -// Template > Resource -type Resource struct { - ApiVersion *string `json:"apiVersion"` - Name *string `json:"name"` - Type *string `json:"type"` - Location *string `json:"location,omitempty"` - DependsOn *[]string `json:"dependsOn,omitempty"` - Plan *Plan `json:"plan,omitempty"` - Properties *Properties `json:"properties,omitempty"` - Tags *map[string]string `json:"tags,omitempty"` - Resources *[]Resource `json:"resources,omitempty"` - Identity *Identity `json:"identity,omitempty"` -} - -type Plan struct { - Name *string `json:"name"` - Product *string `json:"product"` - Publisher *string `json:"publisher"` - PromotionCode *string `json:"promotionCode,omitempty"` -} - -type OSDiskUnion struct { - OsType compute.OperatingSystemTypes `json:"osType,omitempty"` - OsState compute.OperatingSystemStateTypes `json:"osState,omitempty"` - BlobURI *string `json:"blobUri,omitempty"` - Name *string `json:"name,omitempty"` - Vhd *compute.VirtualHardDisk `json:"vhd,omitempty"` - Image *compute.VirtualHardDisk `json:"image,omitempty"` - Caching compute.CachingTypes `json:"caching,omitempty"` - CreateOption compute.DiskCreateOptionTypes `json:"createOption,omitempty"` - DiskSizeGB *int32 `json:"diskSizeGB,omitempty"` - ManagedDisk *compute.ManagedDiskParameters `json:"managedDisk,omitempty"` -} - -type DataDiskUnion struct { - Lun *int `json:"lun,omitempty"` - BlobURI *string `json:"blobUri,omitempty"` - Name *string `json:"name,omitempty"` - Vhd *compute.VirtualHardDisk `json:"vhd,omitempty"` - Image *compute.VirtualHardDisk `json:"image,omitempty"` - Caching compute.CachingTypes `json:"caching,omitempty"` - CreateOption compute.DiskCreateOptionTypes `json:"createOption,omitempty"` - DiskSizeGB *int32 `json:"diskSizeGB,omitempty"` - ManagedDisk *compute.ManagedDiskParameters `json:"managedDisk,omitempty"` -} - -// Union of the StorageProfile and ImageStorageProfile types. -type StorageProfileUnion struct { - ImageReference *compute.ImageReference `json:"imageReference,omitempty"` - OsDisk *OSDiskUnion `json:"osDisk,omitempty"` - DataDisks *[]DataDiskUnion `json:"dataDisks,omitempty"` -} - -///////////////////////////////////////////////// -// Template > Resource > Properties -type Properties struct { - AccessPolicies *[]AccessPolicies `json:"accessPolicies,omitempty"` - AddressSpace *network.AddressSpace `json:"addressSpace,omitempty"` - DiagnosticsProfile *compute.DiagnosticsProfile `json:"diagnosticsProfile,omitempty"` - DNSSettings *network.PublicIPAddressDNSSettings `json:"dnsSettings,omitempty"` - EnabledForDeployment *string `json:"enabledForDeployment,omitempty"` - EnabledForTemplateDeployment *string `json:"enabledForTemplateDeployment,omitempty"` - EnableSoftDelete *string `json:"enableSoftDelete,omitempty"` - HardwareProfile *compute.HardwareProfile `json:"hardwareProfile,omitempty"` - IPConfigurations *[]network.IPConfiguration `json:"ipConfigurations,omitempty"` - NetworkProfile *compute.NetworkProfile `json:"networkProfile,omitempty"` - OsProfile *compute.OSProfile `json:"osProfile,omitempty"` - PublicIPAllocatedMethod *network.IPAllocationMethod `json:"publicIPAllocationMethod,omitempty"` - Sku *Sku `json:"sku,omitempty"` - //StorageProfile3 *compute.StorageProfile `json:"storageProfile,omitempty"` - StorageProfile *StorageProfileUnion `json:"storageProfile,omitempty"` - Subnets *[]network.Subnet `json:"subnets,omitempty"` - SecurityRules *[]network.SecurityRule `json:"securityRules,omitempty"` - TenantId *string `json:"tenantId,omitempty"` - Value *string `json:"value,omitempty"` -} - -// Template > Resource > Identity -// The map values are simplified to struct{} since they are read-only and cannot be set -type Identity struct { - Type *string `json:"type,omitempty"` - UserAssignedIdentities map[string]struct{} `json:"userAssignedIdentities,omitempty"` -} - -type AccessPolicies struct { - ObjectId *string `json:"objectId,omitempty"` - TenantId *string `json:"tenantId,omitempty"` - Permissions *Permissions `json:"permissions,omitempty"` -} - -type Permissions struct { - Keys *[]string `json:"keys,omitempty"` - Secrets *[]string `json:"secrets,omitempty"` -} - -type Sku struct { - Family *string `json:"family,omitempty"` - Name *string `json:"name,omitempty"` -} diff --git a/builder/azure/common/template/template_builder.go b/builder/azure/common/template/template_builder.go deleted file mode 100644 index cdb9f92d1..000000000 --- a/builder/azure/common/template/template_builder.go +++ /dev/null @@ -1,756 +0,0 @@ -package template - -import ( - "encoding/json" - "fmt" - "strconv" - "strings" - - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2018-04-01/compute" - "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2018-01-01/network" - "github.com/Azure/go-autorest/autorest/to" -) - -const ( - jsonPrefix = "" - jsonIndent = " " - - resourceKeyVaults = "Microsoft.KeyVault/vaults" - resourceNetworkInterfaces = "Microsoft.Network/networkInterfaces" - resourcePublicIPAddresses = "Microsoft.Network/publicIPAddresses" - resourceVirtualMachine = "Microsoft.Compute/virtualMachines" - resourceVirtualNetworks = "Microsoft.Network/virtualNetworks" - resourceNetworkSecurityGroups = "Microsoft.Network/networkSecurityGroups" - - variableSshKeyPath = "sshKeyPath" -) - -type TemplateBuilder struct { - template *Template - osType compute.OperatingSystemTypes -} - -func NewTemplateBuilder(template string) (*TemplateBuilder, error) { - var t Template - - err := json.Unmarshal([]byte(template), &t) - if err != nil { - return nil, err - } - - return &TemplateBuilder{ - template: &t, - }, nil -} - -func (s *TemplateBuilder) BuildLinux(sshAuthorizedKey string, disablePasswordAuthentication bool) error { - resource, err := s.getResourceByType(resourceVirtualMachine) - if err != nil { - return err - } - - profile := resource.Properties.OsProfile - profile.LinuxConfiguration = &compute.LinuxConfiguration{ - SSH: &compute.SSHConfiguration{ - PublicKeys: &[]compute.SSHPublicKey{ - { - Path: to.StringPtr(s.toVariable(variableSshKeyPath)), - KeyData: to.StringPtr(sshAuthorizedKey), - }, - }, - }, - } - - if disablePasswordAuthentication { - profile.LinuxConfiguration.DisablePasswordAuthentication = to.BoolPtr(true) - profile.AdminPassword = nil - } - - s.osType = compute.Linux - return nil -} - -func (s *TemplateBuilder) BuildWindows(keyVaultName, winRMCertificateUrl string) error { - resource, err := s.getResourceByType(resourceVirtualMachine) - if err != nil { - return err - } - - profile := resource.Properties.OsProfile - - profile.Secrets = &[]compute.VaultSecretGroup{ - { - SourceVault: &compute.SubResource{ - ID: to.StringPtr(s.toResourceID(resourceKeyVaults, keyVaultName)), - }, - VaultCertificates: &[]compute.VaultCertificate{ - { - CertificateStore: to.StringPtr("My"), - CertificateURL: to.StringPtr(winRMCertificateUrl), - }, - }, - }, - } - - profile.WindowsConfiguration = &compute.WindowsConfiguration{ - ProvisionVMAgent: to.BoolPtr(true), - WinRM: &compute.WinRMConfiguration{ - Listeners: &[]compute.WinRMListener{ - { - Protocol: "https", - CertificateURL: to.StringPtr(winRMCertificateUrl), - }, - }, - }, - } - - s.osType = compute.Windows - return nil -} - -func (s *TemplateBuilder) SetIdentity(userAssignedManagedIdentities []string) error { - resource, err := s.getResourceByType(resourceVirtualMachine) - if err != nil { - return err - } - - var id *Identity - - if len(userAssignedManagedIdentities) != 0 { - s.setVariable("apiVersion", "2018-06-01") // Required for user assigned managed identity - id = &Identity{ - Type: to.StringPtr("UserAssigned"), - UserAssignedIdentities: make(map[string]struct{}), - } - for _, uid := range userAssignedManagedIdentities { - id.UserAssignedIdentities[uid] = struct{}{} - } - } - - resource.Identity = id - return nil -} - -func (s *TemplateBuilder) SetManagedDiskUrl(managedImageId string, storageAccountType compute.StorageAccountTypes, cachingType compute.CachingTypes) error { - resource, err := s.getResourceByType(resourceVirtualMachine) - if err != nil { - return err - } - - profile := resource.Properties.StorageProfile - profile.ImageReference = &compute.ImageReference{ - ID: &managedImageId, - } - profile.OsDisk.OsType = s.osType - profile.OsDisk.CreateOption = compute.DiskCreateOptionTypesFromImage - profile.OsDisk.Vhd = nil - profile.OsDisk.Caching = cachingType - profile.OsDisk.ManagedDisk = &compute.ManagedDiskParameters{ - StorageAccountType: storageAccountType, - } - - return nil -} - -func (s *TemplateBuilder) SetManagedMarketplaceImage(location, publisher, offer, sku, version, imageID string, storageAccountType compute.StorageAccountTypes, cachingType compute.CachingTypes) error { - resource, err := s.getResourceByType(resourceVirtualMachine) - if err != nil { - return err - } - - profile := resource.Properties.StorageProfile - profile.ImageReference = &compute.ImageReference{ - Publisher: &publisher, - Offer: &offer, - Sku: &sku, - Version: &version, - } - profile.OsDisk.OsType = s.osType - profile.OsDisk.CreateOption = compute.DiskCreateOptionTypesFromImage - profile.OsDisk.Vhd = nil - profile.OsDisk.Caching = cachingType - profile.OsDisk.ManagedDisk = &compute.ManagedDiskParameters{ - StorageAccountType: storageAccountType, - } - - return nil -} - -func (s *TemplateBuilder) SetSharedGalleryImage(location, imageID string, cachingType compute.CachingTypes) error { - resource, err := s.getResourceByType(resourceVirtualMachine) - if err != nil { - return err - } - - s.setVariable("apiVersion", "2018-06-01") // Required for Shared Image Gallery - profile := resource.Properties.StorageProfile - profile.ImageReference = &compute.ImageReference{ID: &imageID} - profile.OsDisk.OsType = s.osType - profile.OsDisk.Vhd = nil - profile.OsDisk.Caching = cachingType - - return nil -} - -func (s *TemplateBuilder) SetMarketPlaceImage(publisher, offer, sku, version string, cachingType compute.CachingTypes) error { - resource, err := s.getResourceByType(resourceVirtualMachine) - if err != nil { - return err - } - - profile := resource.Properties.StorageProfile - profile.OsDisk.Caching = cachingType - profile.ImageReference = &compute.ImageReference{ - Publisher: to.StringPtr(publisher), - Offer: to.StringPtr(offer), - Sku: to.StringPtr(sku), - Version: to.StringPtr(version), - } - - return nil -} - -func (s *TemplateBuilder) SetImageUrl(imageUrl string, osType compute.OperatingSystemTypes, cachingType compute.CachingTypes) error { - resource, err := s.getResourceByType(resourceVirtualMachine) - if err != nil { - return err - } - - profile := resource.Properties.StorageProfile - profile.OsDisk.OsType = osType - profile.OsDisk.Caching = cachingType - - profile.OsDisk.Image = &compute.VirtualHardDisk{ - URI: to.StringPtr(imageUrl), - } - - return nil -} - -func (s *TemplateBuilder) SetPlanInfo(name, product, publisher, promotionCode string) error { - var promotionCodeVal *string = nil - if promotionCode != "" { - promotionCodeVal = to.StringPtr(promotionCode) - } - - for i, x := range s.template.Resources { - if strings.EqualFold(*x.Type, resourceVirtualMachine) { - s.template.Resources[i].Plan = &Plan{ - Name: to.StringPtr(name), - Product: to.StringPtr(product), - Publisher: to.StringPtr(publisher), - PromotionCode: promotionCodeVal, - } - } - } - - return nil -} - -func (s *TemplateBuilder) SetOSDiskSizeGB(diskSizeGB int32) error { - resource, err := s.getResourceByType(resourceVirtualMachine) - if err != nil { - return err - } - - profile := resource.Properties.StorageProfile - profile.OsDisk.DiskSizeGB = to.Int32Ptr(diskSizeGB) - - return nil -} - -func (s *TemplateBuilder) SetAdditionalDisks(diskSizeGB []int32, dataDiskname string, isManaged bool, cachingType compute.CachingTypes) error { - resource, err := s.getResourceByType(resourceVirtualMachine) - if err != nil { - return err - } - - profile := resource.Properties.StorageProfile - dataDisks := make([]DataDiskUnion, len(diskSizeGB)) - - for i, additionalSize := range diskSizeGB { - dataDisks[i].DiskSizeGB = to.Int32Ptr(additionalSize) - dataDisks[i].Lun = to.IntPtr(i) - // dataDisks[i].Name = to.StringPtr(fmt.Sprintf("%s-%d", dataDiskname, i+1)) - dataDisks[i].Name = to.StringPtr(fmt.Sprintf("[concat(parameters('dataDiskName'),'-%d')]", i+1)) - dataDisks[i].CreateOption = "Empty" - dataDisks[i].Caching = cachingType - if isManaged { - dataDisks[i].Vhd = nil - dataDisks[i].ManagedDisk = profile.OsDisk.ManagedDisk - } else { - dataDisks[i].Vhd = &compute.VirtualHardDisk{ - URI: to.StringPtr(fmt.Sprintf("[concat(parameters('storageAccountBlobEndpoint'),variables('vmStorageAccountContainerName'),'/',parameters('dataDiskName'),'-%d','.vhd')]", i+1)), - } - dataDisks[i].ManagedDisk = nil - } - } - profile.DataDisks = &dataDisks - return nil -} - -func (s *TemplateBuilder) SetCustomData(customData string) error { - resource, err := s.getResourceByType(resourceVirtualMachine) - if err != nil { - return err - } - - profile := resource.Properties.OsProfile - profile.CustomData = to.StringPtr(customData) - - return nil -} - -func (s *TemplateBuilder) SetVirtualNetwork(virtualNetworkResourceGroup, virtualNetworkName, subnetName string) error { - s.setVariable("virtualNetworkResourceGroup", virtualNetworkResourceGroup) - s.setVariable("virtualNetworkName", virtualNetworkName) - s.setVariable("subnetName", subnetName) - - s.deleteResourceByType(resourceVirtualNetworks) - s.deleteResourceByType(resourcePublicIPAddresses) - resource, err := s.getResourceByType(resourceNetworkInterfaces) - if err != nil { - return err - } - - s.deleteResourceDependency(resource, func(s string) bool { - return strings.Contains(s, "Microsoft.Network/virtualNetworks") || - strings.Contains(s, "Microsoft.Network/publicIPAddresses") - }) - - (*resource.Properties.IPConfigurations)[0].PublicIPAddress = nil - - return nil -} - -func (s *TemplateBuilder) SetPrivateVirtualNetworkWithPublicIp(virtualNetworkResourceGroup, virtualNetworkName, subnetName string) error { - s.setVariable("virtualNetworkResourceGroup", virtualNetworkResourceGroup) - s.setVariable("virtualNetworkName", virtualNetworkName) - s.setVariable("subnetName", subnetName) - - s.deleteResourceByType(resourceVirtualNetworks) - resource, err := s.getResourceByType(resourceNetworkInterfaces) - if err != nil { - return err - } - - s.deleteResourceDependency(resource, func(s string) bool { - return strings.Contains(s, "Microsoft.Network/virtualNetworks") - }) - - return nil -} - -func (s *TemplateBuilder) SetNetworkSecurityGroup(ipAddresses []string, port int) error { - nsgResource, dependency, resourceId := s.createNsgResource(ipAddresses, port) - if err := s.addResource(nsgResource); err != nil { - return err - } - - vnetResource, err := s.getResourceByType(resourceVirtualNetworks) - if err != nil { - return err - } - s.deleteResourceByType(resourceVirtualNetworks) - - s.addResourceDependency(vnetResource, dependency) - - if vnetResource.Properties == nil || vnetResource.Properties.Subnets == nil || len(*vnetResource.Properties.Subnets) != 1 { - return fmt.Errorf("template: could not find virtual network/subnet to add default network security group to") - } - subnet := ((*vnetResource.Properties.Subnets)[0]) - if subnet.SubnetPropertiesFormat == nil { - subnet.SubnetPropertiesFormat = &network.SubnetPropertiesFormat{} - } - if subnet.SubnetPropertiesFormat.NetworkSecurityGroup != nil { - return fmt.Errorf("template: subnet already has an associated network security group") - } - subnet.SubnetPropertiesFormat.NetworkSecurityGroup = &network.SecurityGroup{ - ID: to.StringPtr(resourceId), - } - - s.addResource(vnetResource) - - return nil -} - -func (s *TemplateBuilder) SetTags(tags *map[string]string) error { - if tags == nil || len(*tags) == 0 { - return nil - } - - for i := range s.template.Resources { - s.template.Resources[i].Tags = tags - } - return nil -} - -func (s *TemplateBuilder) SetBootDiagnostics(diagSTG string) error { - - resource, err := s.getResourceByType(resourceVirtualMachine) - if err != nil { - return err - } - - t := true - stg := fmt.Sprintf("https://%s.blob.core.windows.net", diagSTG) - - resource.Properties.DiagnosticsProfile.BootDiagnostics.Enabled = &t - resource.Properties.DiagnosticsProfile.BootDiagnostics.StorageURI = &stg - - return nil -} - -func (s *TemplateBuilder) ToJSON() (*string, error) { - bs, err := json.MarshalIndent(s.template, jsonPrefix, jsonIndent) - - if err != nil { - return nil, err - } - return to.StringPtr(string(bs)), err -} - -func (s *TemplateBuilder) getResourceByType(t string) (*Resource, error) { - for _, x := range s.template.Resources { - if strings.EqualFold(*x.Type, t) { - return x, nil - } - } - - return nil, fmt.Errorf("template: could not find a resource of type %s", t) -} - -func (s *TemplateBuilder) setVariable(name string, value string) { - (*s.template.Variables)[name] = value -} - -func (s *TemplateBuilder) toResourceID(id, name string) string { - return fmt.Sprintf("[resourceId(resourceGroup().name, '%s', '%s')]", id, name) -} - -func (s *TemplateBuilder) toVariable(name string) string { - return fmt.Sprintf("[variables('%s')]", name) -} - -func (s *TemplateBuilder) addResource(newResource *Resource) error { - for _, resource := range s.template.Resources { - if *resource.Type == *newResource.Type { - return fmt.Errorf("template: found an existing resource of type %s", *resource.Type) - } - } - - resources := append(s.template.Resources, newResource) - s.template.Resources = resources - return nil -} - -func (s *TemplateBuilder) deleteResourceByType(resourceType string) { - resources := make([]*Resource, 0) - - for _, resource := range s.template.Resources { - if *resource.Type == resourceType { - continue - } - resources = append(resources, resource) - } - - s.template.Resources = resources -} - -func (s *TemplateBuilder) addResourceDependency(resource *Resource, dep string) { - if resource.DependsOn != nil { - deps := append(*resource.DependsOn, dep) - resource.DependsOn = &deps - } else { - resource.DependsOn = &[]string{dep} - } -} - -func (s *TemplateBuilder) deleteResourceDependency(resource *Resource, predicate func(string) bool) { - deps := make([]string, 0) - - for _, dep := range *resource.DependsOn { - if !predicate(dep) { - deps = append(deps, dep) - } - } - - *resource.DependsOn = deps -} - -func (s *TemplateBuilder) createNsgResource(srcIpAddresses []string, port int) (*Resource, string, string) { - resource := &Resource{ - ApiVersion: to.StringPtr("[variables('networkSecurityGroupsApiVersion')]"), - Name: to.StringPtr("[parameters('nsgName')]"), - Type: to.StringPtr(resourceNetworkSecurityGroups), - Location: to.StringPtr("[variables('location')]"), - Properties: &Properties{ - SecurityRules: &[]network.SecurityRule{ - { - Name: to.StringPtr("AllowIPsToSshWinRMInbound"), - SecurityRulePropertiesFormat: &network.SecurityRulePropertiesFormat{ - Description: to.StringPtr("Allow inbound traffic from specified IP addresses"), - Protocol: network.SecurityRuleProtocolTCP, - Priority: to.Int32Ptr(100), - Access: network.SecurityRuleAccessAllow, - Direction: network.SecurityRuleDirectionInbound, - SourceAddressPrefixes: &srcIpAddresses, - SourcePortRange: to.StringPtr("*"), - DestinationAddressPrefix: to.StringPtr("VirtualNetwork"), - DestinationPortRange: to.StringPtr(strconv.Itoa(port)), - }, - }, - }, - }, - } - - dependency := fmt.Sprintf("[concat('%s/', parameters('nsgName'))]", resourceNetworkSecurityGroups) - resourceId := fmt.Sprintf("[resourceId('%s', parameters('nsgName'))]", resourceNetworkSecurityGroups) - - return resource, dependency, resourceId -} - -// See https://github.com/Azure/azure-quickstart-templates for a extensive list of templates. - -// Template to deploy a KeyVault. -// -// This template is still hard-coded unlike the ARM templates used for VMs for -// a couple of reasons. -// -// 1. The SDK defines no types for a Key Vault -// 2. The Key Vault template is relatively simple, and is static. -// -const KeyVault = `{ - "$schema": "http://schema.management.azure.com/schemas/2014-04-01-preview/deploymentTemplate.json", - "contentVersion": "1.0.0.0", - "parameters": { - "keyVaultName": { - "type": "string" - }, - "keyVaultSKU": { - "type": "string" - }, - "keyVaultSecretValue": { - "type": "securestring" - }, - "objectId": { - "type": "string" - }, - "tenantId": { - "type": "string" - } - }, - "variables": { - "apiVersion": "2015-06-01", - "location": "[resourceGroup().location]", - "keyVaultSecretName": "packerKeyVaultSecret" - }, - "resources": [ - { - "apiVersion": "[variables('apiVersion')]", - "type": "Microsoft.KeyVault/vaults", - "name": "[parameters('keyVaultName')]", - "location": "[variables('location')]", - "properties": { - "enabledForDeployment": "true", - "enabledForTemplateDeployment": "true", - "enableSoftDelete": "true", - "tenantId": "[parameters('tenantId')]", - "accessPolicies": [ - { - "tenantId": "[parameters('tenantId')]", - "objectId": "[parameters('objectId')]", - "permissions": { - "keys": [ "all" ], - "secrets": [ "all" ] - } - } - ], - "sku": { - "name": "[parameters('keyVaultSKU')]", - "family": "A" - } - }, - "resources": [ - { - "apiVersion": "[variables('apiVersion')]", - "type": "secrets", - "name": "[variables('keyVaultSecretName')]", - "dependsOn": [ - "[concat('Microsoft.KeyVault/vaults/', parameters('keyVaultName'))]" - ], - "properties": { - "value": "[parameters('keyVaultSecretValue')]" - } - } - ] - } - ] -}` - -const BasicTemplate = `{ - "$schema": "http://schema.management.azure.com/schemas/2014-04-01-preview/deploymentTemplate.json", - "contentVersion": "1.0.0.0", - "parameters": { - "adminUsername": { - "type": "string" - }, - "adminPassword": { - "type": "string" - }, - "dnsNameForPublicIP": { - "type": "string" - }, - "nicName": { - "type": "string" - }, - "osDiskName": { - "type": "string" - }, - "publicIPAddressName": { - "type": "string" - }, - "subnetName": { - "type": "string" - }, - "storageAccountBlobEndpoint": { - "type": "string" - }, - "virtualNetworkName": { - "type": "string" - }, - "nsgName": { - "type": "string" - }, - "vmSize": { - "type": "string" - }, - "vmName": { - "type": "string" - }, - "dataDiskName": { - "type": "string" - } - }, - "variables": { - "addressPrefix": "10.0.0.0/16", - "apiVersion": "2017-03-30", - "managedDiskApiVersion": "2017-03-30", - "networkInterfacesApiVersion": "2017-04-01", - "publicIPAddressApiVersion": "2017-04-01", - "virtualNetworksApiVersion": "2017-04-01", - "networkSecurityGroupsApiVersion": "2019-04-01", - "location": "[resourceGroup().location]", - "publicIPAddressType": "Dynamic", - "sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]", - "subnetName": "[parameters('subnetName')]", - "subnetAddressPrefix": "10.0.0.0/24", - "subnetRef": "[concat(variables('vnetID'),'/subnets/',variables('subnetName'))]", - "virtualNetworkName": "[parameters('virtualNetworkName')]", - "virtualNetworkResourceGroup": "[resourceGroup().name]", - "vmStorageAccountContainerName": "images", - "vnetID": "[resourceId(variables('virtualNetworkResourceGroup'), 'Microsoft.Network/virtualNetworks', variables('virtualNetworkName'))]" - }, - "resources": [ - { - "apiVersion": "[variables('publicIPAddressApiVersion')]", - "type": "Microsoft.Network/publicIPAddresses", - "name": "[parameters('publicIPAddressName')]", - "location": "[variables('location')]", - "properties": { - "publicIPAllocationMethod": "[variables('publicIPAddressType')]", - "dnsSettings": { - "domainNameLabel": "[parameters('dnsNameForPublicIP')]" - } - } - }, - { - "apiVersion": "[variables('virtualNetworksApiVersion')]", - "type": "Microsoft.Network/virtualNetworks", - "name": "[variables('virtualNetworkName')]", - "location": "[variables('location')]", - "properties": { - "addressSpace": { - "addressPrefixes": [ - "[variables('addressPrefix')]" - ] - }, - "subnets": [ - { - "name": "[variables('subnetName')]", - "properties": { - "addressPrefix": "[variables('subnetAddressPrefix')]" - } - } - ] - } - }, - { - "apiVersion": "[variables('networkInterfacesApiVersion')]", - "type": "Microsoft.Network/networkInterfaces", - "name": "[parameters('nicName')]", - "location": "[variables('location')]", - "dependsOn": [ - "[concat('Microsoft.Network/publicIPAddresses/', parameters('publicIPAddressName'))]", - "[concat('Microsoft.Network/virtualNetworks/', variables('virtualNetworkName'))]" - ], - "properties": { - "ipConfigurations": [ - { - "name": "ipconfig", - "properties": { - "privateIPAllocationMethod": "Dynamic", - "publicIPAddress": { - "id": "[resourceId('Microsoft.Network/publicIPAddresses', parameters('publicIPAddressName'))]" - }, - "subnet": { - "id": "[variables('subnetRef')]" - } - } - } - ] - } - }, - { - "apiVersion": "[variables('apiVersion')]", - "type": "Microsoft.Compute/virtualMachines", - "name": "[parameters('vmName')]", - "location": "[variables('location')]", - "dependsOn": [ - "[concat('Microsoft.Network/networkInterfaces/', parameters('nicName'))]" - ], - "properties": { - "hardwareProfile": { - "vmSize": "[parameters('vmSize')]" - }, - "osProfile": { - "computerName": "[parameters('vmName')]", - "adminUsername": "[parameters('adminUsername')]", - "adminPassword": "[parameters('adminPassword')]" - }, - "storageProfile": { - "osDisk": { - "name": "[parameters('osDiskName')]", - "vhd": { - "uri": "[concat(parameters('storageAccountBlobEndpoint'),variables('vmStorageAccountContainerName'),'/', parameters('osDiskName'),'.vhd')]" - }, - "caching": "ReadWrite", - "createOption": "FromImage" - } - }, - "networkProfile": { - "networkInterfaces": [ - { - "id": "[resourceId('Microsoft.Network/networkInterfaces', parameters('nicName'))]" - } - ] - }, - "diagnosticsProfile": { - "bootDiagnostics": { - "enabled": false - } - } - } - } - ] -}` diff --git a/builder/azure/common/template/template_builder_test.TestBuildLinux00.approved.json b/builder/azure/common/template/template_builder_test.TestBuildLinux00.approved.json deleted file mode 100644 index 403de815c..000000000 --- a/builder/azure/common/template/template_builder_test.TestBuildLinux00.approved.json +++ /dev/null @@ -1,182 +0,0 @@ -{ - "$schema": "http://schema.management.azure.com/schemas/2014-04-01-preview/deploymentTemplate.json", - "contentVersion": "1.0.0.0", - "parameters": { - "adminPassword": { - "type": "string" - }, - "adminUsername": { - "type": "string" - }, - "dataDiskName": { - "type": "string" - }, - "dnsNameForPublicIP": { - "type": "string" - }, - "nicName": { - "type": "string" - }, - "nsgName": { - "type": "string" - }, - "osDiskName": { - "type": "string" - }, - "publicIPAddressName": { - "type": "string" - }, - "storageAccountBlobEndpoint": { - "type": "string" - }, - "subnetName": { - "type": "string" - }, - "virtualNetworkName": { - "type": "string" - }, - "vmName": { - "type": "string" - }, - "vmSize": { - "type": "string" - } - }, - "resources": [ - { - "apiVersion": "[variables('publicIPAddressApiVersion')]", - "location": "[variables('location')]", - "name": "[parameters('publicIPAddressName')]", - "properties": { - "dnsSettings": { - "domainNameLabel": "[parameters('dnsNameForPublicIP')]" - }, - "publicIPAllocationMethod": "[variables('publicIPAddressType')]" - }, - "type": "Microsoft.Network/publicIPAddresses" - }, - { - "apiVersion": "[variables('virtualNetworksApiVersion')]", - "location": "[variables('location')]", - "name": "[variables('virtualNetworkName')]", - "properties": { - "addressSpace": { - "addressPrefixes": [ - "[variables('addressPrefix')]" - ] - }, - "subnets": [ - { - "name": "[variables('subnetName')]", - "properties": { - "addressPrefix": "[variables('subnetAddressPrefix')]" - } - } - ] - }, - "type": "Microsoft.Network/virtualNetworks" - }, - { - "apiVersion": "[variables('networkInterfacesApiVersion')]", - "dependsOn": [ - "[concat('Microsoft.Network/publicIPAddresses/', parameters('publicIPAddressName'))]", - "[concat('Microsoft.Network/virtualNetworks/', variables('virtualNetworkName'))]" - ], - "location": "[variables('location')]", - "name": "[parameters('nicName')]", - "properties": { - "ipConfigurations": [ - { - "name": "ipconfig", - "properties": { - "privateIPAllocationMethod": "Dynamic", - "publicIPAddress": { - "id": "[resourceId('Microsoft.Network/publicIPAddresses', parameters('publicIPAddressName'))]" - }, - "subnet": { - "id": "[variables('subnetRef')]" - } - } - } - ] - }, - "type": "Microsoft.Network/networkInterfaces" - }, - { - "apiVersion": "[variables('apiVersion')]", - "dependsOn": [ - "[concat('Microsoft.Network/networkInterfaces/', parameters('nicName'))]" - ], - "location": "[variables('location')]", - "name": "[parameters('vmName')]", - "properties": { - "diagnosticsProfile": { - "bootDiagnostics": { - "enabled": false - } - }, - "hardwareProfile": { - "vmSize": "[parameters('vmSize')]" - }, - "networkProfile": { - "networkInterfaces": [ - { - "id": "[resourceId('Microsoft.Network/networkInterfaces', parameters('nicName'))]" - } - ] - }, - "osProfile": { - "adminUsername": "[parameters('adminUsername')]", - "computerName": "[parameters('vmName')]", - "linuxConfiguration": { - "disablePasswordAuthentication": true, - "ssh": { - "publicKeys": [ - { - "keyData": "--test-ssh-authorized-key--", - "path": "[variables('sshKeyPath')]" - } - ] - } - } - }, - "storageProfile": { - "imageReference": { - "offer": "UbuntuServer", - "publisher": "Canonical", - "sku": "16.04", - "version": "latest" - }, - "osDisk": { - "caching": "ReadWrite", - "createOption": "FromImage", - "name": "[parameters('osDiskName')]", - "vhd": { - "uri": "[concat(parameters('storageAccountBlobEndpoint'),variables('vmStorageAccountContainerName'),'/', parameters('osDiskName'),'.vhd')]" - } - } - } - }, - "type": "Microsoft.Compute/virtualMachines" - } - ], - "variables": { - "addressPrefix": "10.0.0.0/16", - "apiVersion": "2017-03-30", - "location": "[resourceGroup().location]", - "managedDiskApiVersion": "2017-03-30", - "networkInterfacesApiVersion": "2017-04-01", - "networkSecurityGroupsApiVersion": "2019-04-01", - "publicIPAddressApiVersion": "2017-04-01", - "publicIPAddressType": "Dynamic", - "sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]", - "subnetAddressPrefix": "10.0.0.0/24", - "subnetName": "[parameters('subnetName')]", - "subnetRef": "[concat(variables('vnetID'),'/subnets/',variables('subnetName'))]", - "virtualNetworkName": "[parameters('virtualNetworkName')]", - "virtualNetworkResourceGroup": "[resourceGroup().name]", - "virtualNetworksApiVersion": "2017-04-01", - "vmStorageAccountContainerName": "images", - "vnetID": "[resourceId(variables('virtualNetworkResourceGroup'), 'Microsoft.Network/virtualNetworks', variables('virtualNetworkName'))]" - } -} \ No newline at end of file diff --git a/builder/azure/common/template/template_builder_test.TestBuildLinux01.approved.json b/builder/azure/common/template/template_builder_test.TestBuildLinux01.approved.json deleted file mode 100644 index 981354c91..000000000 --- a/builder/azure/common/template/template_builder_test.TestBuildLinux01.approved.json +++ /dev/null @@ -1,180 +0,0 @@ -{ - "$schema": "http://schema.management.azure.com/schemas/2014-04-01-preview/deploymentTemplate.json", - "contentVersion": "1.0.0.0", - "parameters": { - "adminPassword": { - "type": "string" - }, - "adminUsername": { - "type": "string" - }, - "dataDiskName": { - "type": "string" - }, - "dnsNameForPublicIP": { - "type": "string" - }, - "nicName": { - "type": "string" - }, - "nsgName": { - "type": "string" - }, - "osDiskName": { - "type": "string" - }, - "publicIPAddressName": { - "type": "string" - }, - "storageAccountBlobEndpoint": { - "type": "string" - }, - "subnetName": { - "type": "string" - }, - "virtualNetworkName": { - "type": "string" - }, - "vmName": { - "type": "string" - }, - "vmSize": { - "type": "string" - } - }, - "resources": [ - { - "apiVersion": "[variables('publicIPAddressApiVersion')]", - "location": "[variables('location')]", - "name": "[parameters('publicIPAddressName')]", - "properties": { - "dnsSettings": { - "domainNameLabel": "[parameters('dnsNameForPublicIP')]" - }, - "publicIPAllocationMethod": "[variables('publicIPAddressType')]" - }, - "type": "Microsoft.Network/publicIPAddresses" - }, - { - "apiVersion": "[variables('virtualNetworksApiVersion')]", - "location": "[variables('location')]", - "name": "[variables('virtualNetworkName')]", - "properties": { - "addressSpace": { - "addressPrefixes": [ - "[variables('addressPrefix')]" - ] - }, - "subnets": [ - { - "name": "[variables('subnetName')]", - "properties": { - "addressPrefix": "[variables('subnetAddressPrefix')]" - } - } - ] - }, - "type": "Microsoft.Network/virtualNetworks" - }, - { - "apiVersion": "[variables('networkInterfacesApiVersion')]", - "dependsOn": [ - "[concat('Microsoft.Network/publicIPAddresses/', parameters('publicIPAddressName'))]", - "[concat('Microsoft.Network/virtualNetworks/', variables('virtualNetworkName'))]" - ], - "location": "[variables('location')]", - "name": "[parameters('nicName')]", - "properties": { - "ipConfigurations": [ - { - "name": "ipconfig", - "properties": { - "privateIPAllocationMethod": "Dynamic", - "publicIPAddress": { - "id": "[resourceId('Microsoft.Network/publicIPAddresses', parameters('publicIPAddressName'))]" - }, - "subnet": { - "id": "[variables('subnetRef')]" - } - } - } - ] - }, - "type": "Microsoft.Network/networkInterfaces" - }, - { - "apiVersion": "[variables('apiVersion')]", - "dependsOn": [ - "[concat('Microsoft.Network/networkInterfaces/', parameters('nicName'))]" - ], - "location": "[variables('location')]", - "name": "[parameters('vmName')]", - "properties": { - "diagnosticsProfile": { - "bootDiagnostics": { - "enabled": false - } - }, - "hardwareProfile": { - "vmSize": "[parameters('vmSize')]" - }, - "networkProfile": { - "networkInterfaces": [ - { - "id": "[resourceId('Microsoft.Network/networkInterfaces', parameters('nicName'))]" - } - ] - }, - "osProfile": { - "adminPassword": "[parameters('adminPassword')]", - "adminUsername": "[parameters('adminUsername')]", - "computerName": "[parameters('vmName')]", - "linuxConfiguration": { - "ssh": { - "publicKeys": [ - { - "keyData": "--test-ssh-authorized-key--", - "path": "[variables('sshKeyPath')]" - } - ] - } - } - }, - "storageProfile": { - "osDisk": { - "caching": "ReadWrite", - "createOption": "FromImage", - "image": { - "uri": "http://azure/custom.vhd" - }, - "name": "[parameters('osDiskName')]", - "osType": "Linux", - "vhd": { - "uri": "[concat(parameters('storageAccountBlobEndpoint'),variables('vmStorageAccountContainerName'),'/', parameters('osDiskName'),'.vhd')]" - } - } - } - }, - "type": "Microsoft.Compute/virtualMachines" - } - ], - "variables": { - "addressPrefix": "10.0.0.0/16", - "apiVersion": "2017-03-30", - "location": "[resourceGroup().location]", - "managedDiskApiVersion": "2017-03-30", - "networkInterfacesApiVersion": "2017-04-01", - "networkSecurityGroupsApiVersion": "2019-04-01", - "publicIPAddressApiVersion": "2017-04-01", - "publicIPAddressType": "Dynamic", - "sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]", - "subnetAddressPrefix": "10.0.0.0/24", - "subnetName": "[parameters('subnetName')]", - "subnetRef": "[concat(variables('vnetID'),'/subnets/',variables('subnetName'))]", - "virtualNetworkName": "[parameters('virtualNetworkName')]", - "virtualNetworkResourceGroup": "[resourceGroup().name]", - "virtualNetworksApiVersion": "2017-04-01", - "vmStorageAccountContainerName": "images", - "vnetID": "[resourceId(variables('virtualNetworkResourceGroup'), 'Microsoft.Network/virtualNetworks', variables('virtualNetworkName'))]" - } -} \ No newline at end of file diff --git a/builder/azure/common/template/template_builder_test.TestBuildLinux02.approved.json b/builder/azure/common/template/template_builder_test.TestBuildLinux02.approved.json deleted file mode 100644 index fc1441816..000000000 --- a/builder/azure/common/template/template_builder_test.TestBuildLinux02.approved.json +++ /dev/null @@ -1,142 +0,0 @@ -{ - "$schema": "http://schema.management.azure.com/schemas/2014-04-01-preview/deploymentTemplate.json", - "contentVersion": "1.0.0.0", - "parameters": { - "adminPassword": { - "type": "string" - }, - "adminUsername": { - "type": "string" - }, - "dataDiskName": { - "type": "string" - }, - "dnsNameForPublicIP": { - "type": "string" - }, - "nicName": { - "type": "string" - }, - "nsgName": { - "type": "string" - }, - "osDiskName": { - "type": "string" - }, - "publicIPAddressName": { - "type": "string" - }, - "storageAccountBlobEndpoint": { - "type": "string" - }, - "subnetName": { - "type": "string" - }, - "virtualNetworkName": { - "type": "string" - }, - "vmName": { - "type": "string" - }, - "vmSize": { - "type": "string" - } - }, - "resources": [ - { - "apiVersion": "[variables('networkInterfacesApiVersion')]", - "dependsOn": [], - "location": "[variables('location')]", - "name": "[parameters('nicName')]", - "properties": { - "ipConfigurations": [ - { - "name": "ipconfig", - "properties": { - "privateIPAllocationMethod": "Dynamic", - "subnet": { - "id": "[variables('subnetRef')]" - } - } - } - ] - }, - "type": "Microsoft.Network/networkInterfaces" - }, - { - "apiVersion": "[variables('apiVersion')]", - "dependsOn": [ - "[concat('Microsoft.Network/networkInterfaces/', parameters('nicName'))]" - ], - "location": "[variables('location')]", - "name": "[parameters('vmName')]", - "properties": { - "diagnosticsProfile": { - "bootDiagnostics": { - "enabled": false - } - }, - "hardwareProfile": { - "vmSize": "[parameters('vmSize')]" - }, - "networkProfile": { - "networkInterfaces": [ - { - "id": "[resourceId('Microsoft.Network/networkInterfaces', parameters('nicName'))]" - } - ] - }, - "osProfile": { - "adminUsername": "[parameters('adminUsername')]", - "computerName": "[parameters('vmName')]", - "linuxConfiguration": { - "disablePasswordAuthentication": true, - "ssh": { - "publicKeys": [ - { - "keyData": "--test-ssh-authorized-key--", - "path": "[variables('sshKeyPath')]" - } - ] - } - } - }, - "storageProfile": { - "osDisk": { - "caching": "ReadWrite", - "createOption": "FromImage", - "diskSizeGB": 100, - "image": { - "uri": "http://azure/custom.vhd" - }, - "name": "[parameters('osDiskName')]", - "osType": "Linux", - "vhd": { - "uri": "[concat(parameters('storageAccountBlobEndpoint'),variables('vmStorageAccountContainerName'),'/', parameters('osDiskName'),'.vhd')]" - } - } - } - }, - "type": "Microsoft.Compute/virtualMachines" - } - ], - "variables": { - "addressPrefix": "10.0.0.0/16", - "apiVersion": "2017-03-30", - "location": "[resourceGroup().location]", - "managedDiskApiVersion": "2017-03-30", - "networkInterfacesApiVersion": "2017-04-01", - "networkSecurityGroupsApiVersion": "2019-04-01", - "publicIPAddressApiVersion": "2017-04-01", - "publicIPAddressType": "Dynamic", - "sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]", - "subnetAddressPrefix": "10.0.0.0/24", - "subnetName": "--subnet-name--", - "subnetRef": "[concat(variables('vnetID'),'/subnets/',variables('subnetName'))]", - "virtualNetworkName": "--virtual-network--", - "virtualNetworkResourceGroup": "--virtual-network-resource-group--", - "virtualNetworksApiVersion": "2017-04-01", - "vmStorageAccountContainerName": "images", - "vnetID": "[resourceId(variables('virtualNetworkResourceGroup'), 'Microsoft.Network/virtualNetworks', variables('virtualNetworkName'))]" - } -} \ No newline at end of file diff --git a/builder/azure/common/template/template_builder_test.TestBuildWindows00.approved.json b/builder/azure/common/template/template_builder_test.TestBuildWindows00.approved.json deleted file mode 100644 index 1c29ee885..000000000 --- a/builder/azure/common/template/template_builder_test.TestBuildWindows00.approved.json +++ /dev/null @@ -1,196 +0,0 @@ -{ - "$schema": "http://schema.management.azure.com/schemas/2014-04-01-preview/deploymentTemplate.json", - "contentVersion": "1.0.0.0", - "parameters": { - "adminPassword": { - "type": "string" - }, - "adminUsername": { - "type": "string" - }, - "dataDiskName": { - "type": "string" - }, - "dnsNameForPublicIP": { - "type": "string" - }, - "nicName": { - "type": "string" - }, - "nsgName": { - "type": "string" - }, - "osDiskName": { - "type": "string" - }, - "publicIPAddressName": { - "type": "string" - }, - "storageAccountBlobEndpoint": { - "type": "string" - }, - "subnetName": { - "type": "string" - }, - "virtualNetworkName": { - "type": "string" - }, - "vmName": { - "type": "string" - }, - "vmSize": { - "type": "string" - } - }, - "resources": [ - { - "apiVersion": "[variables('publicIPAddressApiVersion')]", - "location": "[variables('location')]", - "name": "[parameters('publicIPAddressName')]", - "properties": { - "dnsSettings": { - "domainNameLabel": "[parameters('dnsNameForPublicIP')]" - }, - "publicIPAllocationMethod": "[variables('publicIPAddressType')]" - }, - "type": "Microsoft.Network/publicIPAddresses" - }, - { - "apiVersion": "[variables('virtualNetworksApiVersion')]", - "location": "[variables('location')]", - "name": "[variables('virtualNetworkName')]", - "properties": { - "addressSpace": { - "addressPrefixes": [ - "[variables('addressPrefix')]" - ] - }, - "subnets": [ - { - "name": "[variables('subnetName')]", - "properties": { - "addressPrefix": "[variables('subnetAddressPrefix')]" - } - } - ] - }, - "type": "Microsoft.Network/virtualNetworks" - }, - { - "apiVersion": "[variables('networkInterfacesApiVersion')]", - "dependsOn": [ - "[concat('Microsoft.Network/publicIPAddresses/', parameters('publicIPAddressName'))]", - "[concat('Microsoft.Network/virtualNetworks/', variables('virtualNetworkName'))]" - ], - "location": "[variables('location')]", - "name": "[parameters('nicName')]", - "properties": { - "ipConfigurations": [ - { - "name": "ipconfig", - "properties": { - "privateIPAllocationMethod": "Dynamic", - "publicIPAddress": { - "id": "[resourceId('Microsoft.Network/publicIPAddresses', parameters('publicIPAddressName'))]" - }, - "subnet": { - "id": "[variables('subnetRef')]" - } - } - } - ] - }, - "type": "Microsoft.Network/networkInterfaces" - }, - { - "apiVersion": "[variables('apiVersion')]", - "dependsOn": [ - "[concat('Microsoft.Network/networkInterfaces/', parameters('nicName'))]" - ], - "location": "[variables('location')]", - "name": "[parameters('vmName')]", - "properties": { - "diagnosticsProfile": { - "bootDiagnostics": { - "enabled": false - } - }, - "hardwareProfile": { - "vmSize": "[parameters('vmSize')]" - }, - "networkProfile": { - "networkInterfaces": [ - { - "id": "[resourceId('Microsoft.Network/networkInterfaces', parameters('nicName'))]" - } - ] - }, - "osProfile": { - "adminPassword": "[parameters('adminPassword')]", - "adminUsername": "[parameters('adminUsername')]", - "computerName": "[parameters('vmName')]", - "secrets": [ - { - "sourceVault": { - "id": "[resourceId(resourceGroup().name, 'Microsoft.KeyVault/vaults', '--test-key-vault-name')]" - }, - "vaultCertificates": [ - { - "certificateStore": "My", - "certificateUrl": "--test-winrm-certificate-url--" - } - ] - } - ], - "windowsConfiguration": { - "provisionVMAgent": true, - "winRM": { - "listeners": [ - { - "certificateUrl": "--test-winrm-certificate-url--", - "protocol": "https" - } - ] - } - } - }, - "storageProfile": { - "imageReference": { - "offer": "WindowsServer", - "publisher": "MicrosoftWindowsServer", - "sku": "2012-R2-Datacenter", - "version": "latest" - }, - "osDisk": { - "caching": "ReadWrite", - "createOption": "FromImage", - "name": "[parameters('osDiskName')]", - "vhd": { - "uri": "[concat(parameters('storageAccountBlobEndpoint'),variables('vmStorageAccountContainerName'),'/', parameters('osDiskName'),'.vhd')]" - } - } - } - }, - "type": "Microsoft.Compute/virtualMachines" - } - ], - "variables": { - "addressPrefix": "10.0.0.0/16", - "apiVersion": "2017-03-30", - "location": "[resourceGroup().location]", - "managedDiskApiVersion": "2017-03-30", - "networkInterfacesApiVersion": "2017-04-01", - "networkSecurityGroupsApiVersion": "2019-04-01", - "publicIPAddressApiVersion": "2017-04-01", - "publicIPAddressType": "Dynamic", - "sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]", - "subnetAddressPrefix": "10.0.0.0/24", - "subnetName": "[parameters('subnetName')]", - "subnetRef": "[concat(variables('vnetID'),'/subnets/',variables('subnetName'))]", - "virtualNetworkName": "[parameters('virtualNetworkName')]", - "virtualNetworkResourceGroup": "[resourceGroup().name]", - "virtualNetworksApiVersion": "2017-04-01", - "vmStorageAccountContainerName": "images", - "vnetID": "[resourceId(variables('virtualNetworkResourceGroup'), 'Microsoft.Network/virtualNetworks', variables('virtualNetworkName'))]" - } -} \ No newline at end of file diff --git a/builder/azure/common/template/template_builder_test.TestBuildWindows01.approved.json b/builder/azure/common/template/template_builder_test.TestBuildWindows01.approved.json deleted file mode 100644 index 9c094ef04..000000000 --- a/builder/azure/common/template/template_builder_test.TestBuildWindows01.approved.json +++ /dev/null @@ -1,219 +0,0 @@ -{ - "$schema": "http://schema.management.azure.com/schemas/2014-04-01-preview/deploymentTemplate.json", - "contentVersion": "1.0.0.0", - "parameters": { - "adminPassword": { - "type": "string" - }, - "adminUsername": { - "type": "string" - }, - "dataDiskName": { - "type": "string" - }, - "dnsNameForPublicIP": { - "type": "string" - }, - "nicName": { - "type": "string" - }, - "nsgName": { - "type": "string" - }, - "osDiskName": { - "type": "string" - }, - "publicIPAddressName": { - "type": "string" - }, - "storageAccountBlobEndpoint": { - "type": "string" - }, - "subnetName": { - "type": "string" - }, - "virtualNetworkName": { - "type": "string" - }, - "vmName": { - "type": "string" - }, - "vmSize": { - "type": "string" - } - }, - "resources": [ - { - "apiVersion": "[variables('publicIPAddressApiVersion')]", - "location": "[variables('location')]", - "name": "[parameters('publicIPAddressName')]", - "properties": { - "dnsSettings": { - "domainNameLabel": "[parameters('dnsNameForPublicIP')]" - }, - "publicIPAllocationMethod": "[variables('publicIPAddressType')]" - }, - "type": "Microsoft.Network/publicIPAddresses" - }, - { - "apiVersion": "[variables('virtualNetworksApiVersion')]", - "location": "[variables('location')]", - "name": "[variables('virtualNetworkName')]", - "properties": { - "addressSpace": { - "addressPrefixes": [ - "[variables('addressPrefix')]" - ] - }, - "subnets": [ - { - "name": "[variables('subnetName')]", - "properties": { - "addressPrefix": "[variables('subnetAddressPrefix')]" - } - } - ] - }, - "type": "Microsoft.Network/virtualNetworks" - }, - { - "apiVersion": "[variables('networkInterfacesApiVersion')]", - "dependsOn": [ - "[concat('Microsoft.Network/publicIPAddresses/', parameters('publicIPAddressName'))]", - "[concat('Microsoft.Network/virtualNetworks/', variables('virtualNetworkName'))]" - ], - "location": "[variables('location')]", - "name": "[parameters('nicName')]", - "properties": { - "ipConfigurations": [ - { - "name": "ipconfig", - "properties": { - "privateIPAllocationMethod": "Dynamic", - "publicIPAddress": { - "id": "[resourceId('Microsoft.Network/publicIPAddresses', parameters('publicIPAddressName'))]" - }, - "subnet": { - "id": "[variables('subnetRef')]" - } - } - } - ] - }, - "type": "Microsoft.Network/networkInterfaces" - }, - { - "apiVersion": "[variables('apiVersion')]", - "dependsOn": [ - "[concat('Microsoft.Network/networkInterfaces/', parameters('nicName'))]" - ], - "location": "[variables('location')]", - "name": "[parameters('vmName')]", - "properties": { - "diagnosticsProfile": { - "bootDiagnostics": { - "enabled": false - } - }, - "hardwareProfile": { - "vmSize": "[parameters('vmSize')]" - }, - "networkProfile": { - "networkInterfaces": [ - { - "id": "[resourceId('Microsoft.Network/networkInterfaces', parameters('nicName'))]" - } - ] - }, - "osProfile": { - "adminPassword": "[parameters('adminPassword')]", - "adminUsername": "[parameters('adminUsername')]", - "computerName": "[parameters('vmName')]", - "secrets": [ - { - "sourceVault": { - "id": "[resourceId(resourceGroup().name, 'Microsoft.KeyVault/vaults', '--test-key-vault-name')]" - }, - "vaultCertificates": [ - { - "certificateStore": "My", - "certificateUrl": "--test-winrm-certificate-url--" - } - ] - } - ], - "windowsConfiguration": { - "provisionVMAgent": true, - "winRM": { - "listeners": [ - { - "certificateUrl": "--test-winrm-certificate-url--", - "protocol": "https" - } - ] - } - } - }, - "storageProfile": { - "dataDisks": [ - { - "caching": "ReadWrite", - "createOption": "Empty", - "diskSizeGB": 32, - "lun": 0, - "managedDisk": { - "storageAccountType": "Premium_LRS" - }, - "name": "[concat(parameters('dataDiskName'),'-1')]" - }, - { - "caching": "ReadWrite", - "createOption": "Empty", - "diskSizeGB": 64, - "lun": 1, - "managedDisk": { - "storageAccountType": "Premium_LRS" - }, - "name": "[concat(parameters('dataDiskName'),'-2')]" - } - ], - "imageReference": { - "offer": "2012-R2-Datacenter", - "publisher": "WindowsServer", - "sku": "latest", - "version": "2015-1" - }, - "osDisk": { - "caching": "ReadWrite", - "createOption": "FromImage", - "managedDisk": { - "storageAccountType": "Premium_LRS" - }, - "name": "[parameters('osDiskName')]", - "osType": "Windows" - } - } - }, - "type": "Microsoft.Compute/virtualMachines" - } - ], - "variables": { - "addressPrefix": "10.0.0.0/16", - "apiVersion": "2017-03-30", - "location": "[resourceGroup().location]", - "managedDiskApiVersion": "2017-03-30", - "networkInterfacesApiVersion": "2017-04-01", - "networkSecurityGroupsApiVersion": "2019-04-01", - "publicIPAddressApiVersion": "2017-04-01", - "publicIPAddressType": "Dynamic", - "sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]", - "subnetAddressPrefix": "10.0.0.0/24", - "subnetName": "[parameters('subnetName')]", - "subnetRef": "[concat(variables('vnetID'),'/subnets/',variables('subnetName'))]", - "virtualNetworkName": "[parameters('virtualNetworkName')]", - "virtualNetworkResourceGroup": "[resourceGroup().name]", - "virtualNetworksApiVersion": "2017-04-01", - "vmStorageAccountContainerName": "images", - "vnetID": "[resourceId(variables('virtualNetworkResourceGroup'), 'Microsoft.Network/virtualNetworks', variables('virtualNetworkName'))]" - } -} \ No newline at end of file diff --git a/builder/azure/common/template/template_builder_test.TestBuildWindows02.approved.json b/builder/azure/common/template/template_builder_test.TestBuildWindows02.approved.json deleted file mode 100644 index 281b8db83..000000000 --- a/builder/azure/common/template/template_builder_test.TestBuildWindows02.approved.json +++ /dev/null @@ -1,212 +0,0 @@ -{ - "$schema": "http://schema.management.azure.com/schemas/2014-04-01-preview/deploymentTemplate.json", - "contentVersion": "1.0.0.0", - "parameters": { - "adminPassword": { - "type": "string" - }, - "adminUsername": { - "type": "string" - }, - "dataDiskName": { - "type": "string" - }, - "dnsNameForPublicIP": { - "type": "string" - }, - "nicName": { - "type": "string" - }, - "nsgName": { - "type": "string" - }, - "osDiskName": { - "type": "string" - }, - "publicIPAddressName": { - "type": "string" - }, - "storageAccountBlobEndpoint": { - "type": "string" - }, - "subnetName": { - "type": "string" - }, - "virtualNetworkName": { - "type": "string" - }, - "vmName": { - "type": "string" - }, - "vmSize": { - "type": "string" - } - }, - "resources": [ - { - "apiVersion": "[variables('publicIPAddressApiVersion')]", - "location": "[variables('location')]", - "name": "[parameters('publicIPAddressName')]", - "properties": { - "dnsSettings": { - "domainNameLabel": "[parameters('dnsNameForPublicIP')]" - }, - "publicIPAllocationMethod": "[variables('publicIPAddressType')]" - }, - "type": "Microsoft.Network/publicIPAddresses" - }, - { - "apiVersion": "[variables('virtualNetworksApiVersion')]", - "location": "[variables('location')]", - "name": "[variables('virtualNetworkName')]", - "properties": { - "addressSpace": { - "addressPrefixes": [ - "[variables('addressPrefix')]" - ] - }, - "subnets": [ - { - "name": "[variables('subnetName')]", - "properties": { - "addressPrefix": "[variables('subnetAddressPrefix')]" - } - } - ] - }, - "type": "Microsoft.Network/virtualNetworks" - }, - { - "apiVersion": "[variables('networkInterfacesApiVersion')]", - "dependsOn": [ - "[concat('Microsoft.Network/publicIPAddresses/', parameters('publicIPAddressName'))]", - "[concat('Microsoft.Network/virtualNetworks/', variables('virtualNetworkName'))]" - ], - "location": "[variables('location')]", - "name": "[parameters('nicName')]", - "properties": { - "ipConfigurations": [ - { - "name": "ipconfig", - "properties": { - "privateIPAllocationMethod": "Dynamic", - "publicIPAddress": { - "id": "[resourceId('Microsoft.Network/publicIPAddresses', parameters('publicIPAddressName'))]" - }, - "subnet": { - "id": "[variables('subnetRef')]" - } - } - } - ] - }, - "type": "Microsoft.Network/networkInterfaces" - }, - { - "apiVersion": "[variables('apiVersion')]", - "dependsOn": [ - "[concat('Microsoft.Network/networkInterfaces/', parameters('nicName'))]" - ], - "location": "[variables('location')]", - "name": "[parameters('vmName')]", - "properties": { - "diagnosticsProfile": { - "bootDiagnostics": { - "enabled": false - } - }, - "hardwareProfile": { - "vmSize": "[parameters('vmSize')]" - }, - "networkProfile": { - "networkInterfaces": [ - { - "id": "[resourceId('Microsoft.Network/networkInterfaces', parameters('nicName'))]" - } - ] - }, - "osProfile": { - "adminPassword": "[parameters('adminPassword')]", - "adminUsername": "[parameters('adminUsername')]", - "computerName": "[parameters('vmName')]", - "secrets": [ - { - "sourceVault": { - "id": "[resourceId(resourceGroup().name, 'Microsoft.KeyVault/vaults', '--test-key-vault-name')]" - }, - "vaultCertificates": [ - { - "certificateStore": "My", - "certificateUrl": "--test-winrm-certificate-url--" - } - ] - } - ], - "windowsConfiguration": { - "provisionVMAgent": true, - "winRM": { - "listeners": [ - { - "certificateUrl": "--test-winrm-certificate-url--", - "protocol": "https" - } - ] - } - } - }, - "storageProfile": { - "dataDisks": [ - { - "caching": "ReadWrite", - "createOption": "Empty", - "diskSizeGB": 32, - "lun": 0, - "name": "[concat(parameters('dataDiskName'),'-1')]", - "vhd": { - "uri": "[concat(parameters('storageAccountBlobEndpoint'),variables('vmStorageAccountContainerName'),'/',parameters('dataDiskName'),'-1','.vhd')]" - } - }, - { - "caching": "ReadWrite", - "createOption": "Empty", - "diskSizeGB": 64, - "lun": 1, - "name": "[concat(parameters('dataDiskName'),'-2')]", - "vhd": { - "uri": "[concat(parameters('storageAccountBlobEndpoint'),variables('vmStorageAccountContainerName'),'/',parameters('dataDiskName'),'-2','.vhd')]" - } - } - ], - "osDisk": { - "caching": "ReadWrite", - "createOption": "FromImage", - "name": "[parameters('osDiskName')]", - "vhd": { - "uri": "[concat(parameters('storageAccountBlobEndpoint'),variables('vmStorageAccountContainerName'),'/', parameters('osDiskName'),'.vhd')]" - } - } - } - }, - "type": "Microsoft.Compute/virtualMachines" - } - ], - "variables": { - "addressPrefix": "10.0.0.0/16", - "apiVersion": "2017-03-30", - "location": "[resourceGroup().location]", - "managedDiskApiVersion": "2017-03-30", - "networkInterfacesApiVersion": "2017-04-01", - "networkSecurityGroupsApiVersion": "2019-04-01", - "publicIPAddressApiVersion": "2017-04-01", - "publicIPAddressType": "Dynamic", - "sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]", - "subnetAddressPrefix": "10.0.0.0/24", - "subnetName": "[parameters('subnetName')]", - "subnetRef": "[concat(variables('vnetID'),'/subnets/',variables('subnetName'))]", - "virtualNetworkName": "[parameters('virtualNetworkName')]", - "virtualNetworkResourceGroup": "[resourceGroup().name]", - "virtualNetworksApiVersion": "2017-04-01", - "vmStorageAccountContainerName": "images", - "vnetID": "[resourceId(variables('virtualNetworkResourceGroup'), 'Microsoft.Network/virtualNetworks', variables('virtualNetworkName'))]" - } -} \ No newline at end of file diff --git a/builder/azure/common/template/template_builder_test.TestNetworkSecurityGroup00.approved.json b/builder/azure/common/template/template_builder_test.TestNetworkSecurityGroup00.approved.json deleted file mode 100644 index 7415524d7..000000000 --- a/builder/azure/common/template/template_builder_test.TestNetworkSecurityGroup00.approved.json +++ /dev/null @@ -1,215 +0,0 @@ -{ - "$schema": "http://schema.management.azure.com/schemas/2014-04-01-preview/deploymentTemplate.json", - "contentVersion": "1.0.0.0", - "parameters": { - "adminPassword": { - "type": "string" - }, - "adminUsername": { - "type": "string" - }, - "dataDiskName": { - "type": "string" - }, - "dnsNameForPublicIP": { - "type": "string" - }, - "nicName": { - "type": "string" - }, - "nsgName": { - "type": "string" - }, - "osDiskName": { - "type": "string" - }, - "publicIPAddressName": { - "type": "string" - }, - "storageAccountBlobEndpoint": { - "type": "string" - }, - "subnetName": { - "type": "string" - }, - "virtualNetworkName": { - "type": "string" - }, - "vmName": { - "type": "string" - }, - "vmSize": { - "type": "string" - } - }, - "resources": [ - { - "apiVersion": "[variables('publicIPAddressApiVersion')]", - "location": "[variables('location')]", - "name": "[parameters('publicIPAddressName')]", - "properties": { - "dnsSettings": { - "domainNameLabel": "[parameters('dnsNameForPublicIP')]" - }, - "publicIPAllocationMethod": "[variables('publicIPAddressType')]" - }, - "type": "Microsoft.Network/publicIPAddresses" - }, - { - "apiVersion": "[variables('networkInterfacesApiVersion')]", - "dependsOn": [ - "[concat('Microsoft.Network/publicIPAddresses/', parameters('publicIPAddressName'))]", - "[concat('Microsoft.Network/virtualNetworks/', variables('virtualNetworkName'))]" - ], - "location": "[variables('location')]", - "name": "[parameters('nicName')]", - "properties": { - "ipConfigurations": [ - { - "name": "ipconfig", - "properties": { - "privateIPAllocationMethod": "Dynamic", - "publicIPAddress": { - "id": "[resourceId('Microsoft.Network/publicIPAddresses', parameters('publicIPAddressName'))]" - }, - "subnet": { - "id": "[variables('subnetRef')]" - } - } - } - ] - }, - "type": "Microsoft.Network/networkInterfaces" - }, - { - "apiVersion": "[variables('apiVersion')]", - "dependsOn": [ - "[concat('Microsoft.Network/networkInterfaces/', parameters('nicName'))]" - ], - "location": "[variables('location')]", - "name": "[parameters('vmName')]", - "properties": { - "diagnosticsProfile": { - "bootDiagnostics": { - "enabled": false - } - }, - "hardwareProfile": { - "vmSize": "[parameters('vmSize')]" - }, - "networkProfile": { - "networkInterfaces": [ - { - "id": "[resourceId('Microsoft.Network/networkInterfaces', parameters('nicName'))]" - } - ] - }, - "osProfile": { - "adminPassword": "[parameters('adminPassword')]", - "adminUsername": "[parameters('adminUsername')]", - "computerName": "[parameters('vmName')]", - "linuxConfiguration": { - "ssh": { - "publicKeys": [ - { - "keyData": "--test-ssh-authorized-key--", - "path": "[variables('sshKeyPath')]" - } - ] - } - } - }, - "storageProfile": { - "imageReference": { - "offer": "UbuntuServer", - "publisher": "Canonical", - "sku": "16.04", - "version": "latest" - }, - "osDisk": { - "caching": "ReadWrite", - "createOption": "FromImage", - "name": "[parameters('osDiskName')]", - "vhd": { - "uri": "[concat(parameters('storageAccountBlobEndpoint'),variables('vmStorageAccountContainerName'),'/', parameters('osDiskName'),'.vhd')]" - } - } - } - }, - "type": "Microsoft.Compute/virtualMachines" - }, - { - "apiVersion": "[variables('networkSecurityGroupsApiVersion')]", - "location": "[variables('location')]", - "name": "[parameters('nsgName')]", - "properties": { - "securityRules": [ - { - "name": "AllowIPsToSshWinRMInbound", - "properties": { - "access": "Allow", - "description": "Allow inbound traffic from specified IP addresses", - "destinationAddressPrefix": "VirtualNetwork", - "destinationPortRange": "123", - "direction": "Inbound", - "priority": 100, - "protocol": "Tcp", - "sourceAddressPrefixes": [ - "127.0.0.1", - "192.168.100.0/24" - ], - "sourcePortRange": "*" - } - } - ] - }, - "type": "Microsoft.Network/networkSecurityGroups" - }, - { - "apiVersion": "[variables('virtualNetworksApiVersion')]", - "dependsOn": [ - "[concat('Microsoft.Network/networkSecurityGroups/', parameters('nsgName'))]" - ], - "location": "[variables('location')]", - "name": "[variables('virtualNetworkName')]", - "properties": { - "addressSpace": { - "addressPrefixes": [ - "[variables('addressPrefix')]" - ] - }, - "subnets": [ - { - "name": "[variables('subnetName')]", - "properties": { - "addressPrefix": "[variables('subnetAddressPrefix')]", - "networkSecurityGroup": { - "id": "[resourceId('Microsoft.Network/networkSecurityGroups', parameters('nsgName'))]" - } - } - } - ] - }, - "type": "Microsoft.Network/virtualNetworks" - } - ], - "variables": { - "addressPrefix": "10.0.0.0/16", - "apiVersion": "2017-03-30", - "location": "[resourceGroup().location]", - "managedDiskApiVersion": "2017-03-30", - "networkInterfacesApiVersion": "2017-04-01", - "networkSecurityGroupsApiVersion": "2019-04-01", - "publicIPAddressApiVersion": "2017-04-01", - "publicIPAddressType": "Dynamic", - "sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]", - "subnetAddressPrefix": "10.0.0.0/24", - "subnetName": "[parameters('subnetName')]", - "subnetRef": "[concat(variables('vnetID'),'/subnets/',variables('subnetName'))]", - "virtualNetworkName": "[parameters('virtualNetworkName')]", - "virtualNetworkResourceGroup": "[resourceGroup().name]", - "virtualNetworksApiVersion": "2017-04-01", - "vmStorageAccountContainerName": "images", - "vnetID": "[resourceId(variables('virtualNetworkResourceGroup'), 'Microsoft.Network/virtualNetworks', variables('virtualNetworkName'))]" - } -} \ No newline at end of file diff --git a/builder/azure/common/template/template_builder_test.TestSetIdentity00.approved.json b/builder/azure/common/template/template_builder_test.TestSetIdentity00.approved.json deleted file mode 100644 index 606ebadba..000000000 --- a/builder/azure/common/template/template_builder_test.TestSetIdentity00.approved.json +++ /dev/null @@ -1,188 +0,0 @@ -{ - "$schema": "http://schema.management.azure.com/schemas/2014-04-01-preview/deploymentTemplate.json", - "contentVersion": "1.0.0.0", - "parameters": { - "adminPassword": { - "type": "string" - }, - "adminUsername": { - "type": "string" - }, - "dataDiskName": { - "type": "string" - }, - "dnsNameForPublicIP": { - "type": "string" - }, - "nicName": { - "type": "string" - }, - "nsgName": { - "type": "string" - }, - "osDiskName": { - "type": "string" - }, - "publicIPAddressName": { - "type": "string" - }, - "storageAccountBlobEndpoint": { - "type": "string" - }, - "subnetName": { - "type": "string" - }, - "virtualNetworkName": { - "type": "string" - }, - "vmName": { - "type": "string" - }, - "vmSize": { - "type": "string" - } - }, - "resources": [ - { - "apiVersion": "[variables('publicIPAddressApiVersion')]", - "location": "[variables('location')]", - "name": "[parameters('publicIPAddressName')]", - "properties": { - "dnsSettings": { - "domainNameLabel": "[parameters('dnsNameForPublicIP')]" - }, - "publicIPAllocationMethod": "[variables('publicIPAddressType')]" - }, - "type": "Microsoft.Network/publicIPAddresses" - }, - { - "apiVersion": "[variables('virtualNetworksApiVersion')]", - "location": "[variables('location')]", - "name": "[variables('virtualNetworkName')]", - "properties": { - "addressSpace": { - "addressPrefixes": [ - "[variables('addressPrefix')]" - ] - }, - "subnets": [ - { - "name": "[variables('subnetName')]", - "properties": { - "addressPrefix": "[variables('subnetAddressPrefix')]" - } - } - ] - }, - "type": "Microsoft.Network/virtualNetworks" - }, - { - "apiVersion": "[variables('networkInterfacesApiVersion')]", - "dependsOn": [ - "[concat('Microsoft.Network/publicIPAddresses/', parameters('publicIPAddressName'))]", - "[concat('Microsoft.Network/virtualNetworks/', variables('virtualNetworkName'))]" - ], - "location": "[variables('location')]", - "name": "[parameters('nicName')]", - "properties": { - "ipConfigurations": [ - { - "name": "ipconfig", - "properties": { - "privateIPAllocationMethod": "Dynamic", - "publicIPAddress": { - "id": "[resourceId('Microsoft.Network/publicIPAddresses', parameters('publicIPAddressName'))]" - }, - "subnet": { - "id": "[variables('subnetRef')]" - } - } - } - ] - }, - "type": "Microsoft.Network/networkInterfaces" - }, - { - "apiVersion": "[variables('apiVersion')]", - "dependsOn": [ - "[concat('Microsoft.Network/networkInterfaces/', parameters('nicName'))]" - ], - "identity": { - "type": "UserAssigned", - "userAssignedIdentities": { - "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg1/providers/Microsoft.ManagedIdentity/userAssignedIdentities/id": {} - } - }, - "location": "[variables('location')]", - "name": "[parameters('vmName')]", - "properties": { - "diagnosticsProfile": { - "bootDiagnostics": { - "enabled": false - } - }, - "hardwareProfile": { - "vmSize": "[parameters('vmSize')]" - }, - "networkProfile": { - "networkInterfaces": [ - { - "id": "[resourceId('Microsoft.Network/networkInterfaces', parameters('nicName'))]" - } - ] - }, - "osProfile": { - "adminUsername": "[parameters('adminUsername')]", - "computerName": "[parameters('vmName')]", - "linuxConfiguration": { - "disablePasswordAuthentication": true, - "ssh": { - "publicKeys": [ - { - "keyData": "--test-ssh-authorized-key--", - "path": "[variables('sshKeyPath')]" - } - ] - } - } - }, - "storageProfile": { - "imageReference": { - "offer": "UbuntuServer", - "publisher": "Canonical", - "sku": "16.04", - "version": "latest" - }, - "osDisk": { - "caching": "ReadWrite", - "createOption": "FromImage", - "name": "[parameters('osDiskName')]", - "vhd": { - "uri": "[concat(parameters('storageAccountBlobEndpoint'),variables('vmStorageAccountContainerName'),'/', parameters('osDiskName'),'.vhd')]" - } - } - } - }, - "type": "Microsoft.Compute/virtualMachines" - } - ], - "variables": { - "addressPrefix": "10.0.0.0/16", - "apiVersion": "2018-06-01", - "location": "[resourceGroup().location]", - "managedDiskApiVersion": "2017-03-30", - "networkInterfacesApiVersion": "2017-04-01", - "networkSecurityGroupsApiVersion": "2019-04-01", - "publicIPAddressApiVersion": "2017-04-01", - "publicIPAddressType": "Dynamic", - "sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]", - "subnetAddressPrefix": "10.0.0.0/24", - "subnetName": "[parameters('subnetName')]", - "subnetRef": "[concat(variables('vnetID'),'/subnets/',variables('subnetName'))]", - "virtualNetworkName": "[parameters('virtualNetworkName')]", - "virtualNetworkResourceGroup": "[resourceGroup().name]", - "virtualNetworksApiVersion": "2017-04-01", - "vmStorageAccountContainerName": "images", - "vnetID": "[resourceId(variables('virtualNetworkResourceGroup'), 'Microsoft.Network/virtualNetworks', variables('virtualNetworkName'))]" - } -} \ No newline at end of file diff --git a/builder/azure/common/template/template_builder_test.TestSharedImageGallery00.approved.json b/builder/azure/common/template/template_builder_test.TestSharedImageGallery00.approved.json deleted file mode 100644 index 98602fd16..000000000 --- a/builder/azure/common/template/template_builder_test.TestSharedImageGallery00.approved.json +++ /dev/null @@ -1,177 +0,0 @@ -{ - "$schema": "http://schema.management.azure.com/schemas/2014-04-01-preview/deploymentTemplate.json", - "contentVersion": "1.0.0.0", - "parameters": { - "adminPassword": { - "type": "string" - }, - "adminUsername": { - "type": "string" - }, - "dataDiskName": { - "type": "string" - }, - "dnsNameForPublicIP": { - "type": "string" - }, - "nicName": { - "type": "string" - }, - "nsgName": { - "type": "string" - }, - "osDiskName": { - "type": "string" - }, - "publicIPAddressName": { - "type": "string" - }, - "storageAccountBlobEndpoint": { - "type": "string" - }, - "subnetName": { - "type": "string" - }, - "virtualNetworkName": { - "type": "string" - }, - "vmName": { - "type": "string" - }, - "vmSize": { - "type": "string" - } - }, - "resources": [ - { - "apiVersion": "[variables('publicIPAddressApiVersion')]", - "location": "[variables('location')]", - "name": "[parameters('publicIPAddressName')]", - "properties": { - "dnsSettings": { - "domainNameLabel": "[parameters('dnsNameForPublicIP')]" - }, - "publicIPAllocationMethod": "[variables('publicIPAddressType')]" - }, - "type": "Microsoft.Network/publicIPAddresses" - }, - { - "apiVersion": "[variables('virtualNetworksApiVersion')]", - "location": "[variables('location')]", - "name": "[variables('virtualNetworkName')]", - "properties": { - "addressSpace": { - "addressPrefixes": [ - "[variables('addressPrefix')]" - ] - }, - "subnets": [ - { - "name": "[variables('subnetName')]", - "properties": { - "addressPrefix": "[variables('subnetAddressPrefix')]" - } - } - ] - }, - "type": "Microsoft.Network/virtualNetworks" - }, - { - "apiVersion": "[variables('networkInterfacesApiVersion')]", - "dependsOn": [ - "[concat('Microsoft.Network/publicIPAddresses/', parameters('publicIPAddressName'))]", - "[concat('Microsoft.Network/virtualNetworks/', variables('virtualNetworkName'))]" - ], - "location": "[variables('location')]", - "name": "[parameters('nicName')]", - "properties": { - "ipConfigurations": [ - { - "name": "ipconfig", - "properties": { - "privateIPAllocationMethod": "Dynamic", - "publicIPAddress": { - "id": "[resourceId('Microsoft.Network/publicIPAddresses', parameters('publicIPAddressName'))]" - }, - "subnet": { - "id": "[variables('subnetRef')]" - } - } - } - ] - }, - "type": "Microsoft.Network/networkInterfaces" - }, - { - "apiVersion": "[variables('apiVersion')]", - "dependsOn": [ - "[concat('Microsoft.Network/networkInterfaces/', parameters('nicName'))]" - ], - "location": "[variables('location')]", - "name": "[parameters('vmName')]", - "properties": { - "diagnosticsProfile": { - "bootDiagnostics": { - "enabled": false - } - }, - "hardwareProfile": { - "vmSize": "[parameters('vmSize')]" - }, - "networkProfile": { - "networkInterfaces": [ - { - "id": "[resourceId('Microsoft.Network/networkInterfaces', parameters('nicName'))]" - } - ] - }, - "osProfile": { - "adminPassword": "[parameters('adminPassword')]", - "adminUsername": "[parameters('adminUsername')]", - "computerName": "[parameters('vmName')]", - "linuxConfiguration": { - "ssh": { - "publicKeys": [ - { - "keyData": "--test-ssh-authorized-key--", - "path": "[variables('sshKeyPath')]" - } - ] - } - } - }, - "storageProfile": { - "imageReference": { - "id": "/subscriptions/ignore/resourceGroups/ignore/providers/Microsoft.Compute/galleries/ignore/images/ignore" - }, - "osDisk": { - "caching": "ReadOnly", - "createOption": "FromImage", - "name": "[parameters('osDiskName')]", - "osType": "Linux" - } - } - }, - "type": "Microsoft.Compute/virtualMachines" - } - ], - "variables": { - "addressPrefix": "10.0.0.0/16", - "apiVersion": "2018-06-01", - "location": "[resourceGroup().location]", - "managedDiskApiVersion": "2017-03-30", - "networkInterfacesApiVersion": "2017-04-01", - "networkSecurityGroupsApiVersion": "2019-04-01", - "publicIPAddressApiVersion": "2017-04-01", - "publicIPAddressType": "Dynamic", - "sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]", - "subnetAddressPrefix": "10.0.0.0/24", - "subnetName": "[parameters('subnetName')]", - "subnetRef": "[concat(variables('vnetID'),'/subnets/',variables('subnetName'))]", - "virtualNetworkName": "[parameters('virtualNetworkName')]", - "virtualNetworkResourceGroup": "[resourceGroup().name]", - "virtualNetworksApiVersion": "2017-04-01", - "vmStorageAccountContainerName": "images", - "vnetID": "[resourceId(variables('virtualNetworkResourceGroup'), 'Microsoft.Network/virtualNetworks', variables('virtualNetworkName'))]" - } -} \ No newline at end of file diff --git a/builder/azure/common/template/template_builder_test.go b/builder/azure/common/template/template_builder_test.go deleted file mode 100644 index 94a4b3673..000000000 --- a/builder/azure/common/template/template_builder_test.go +++ /dev/null @@ -1,283 +0,0 @@ -package template - -import ( - "testing" - - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2018-04-01/compute" - approvaltests "github.com/approvals/go-approval-tests" -) - -// Ensure that a Linux template is configured as expected. -// * Include SSH configuration: authorized key, and key path. -func TestBuildLinux00(t *testing.T) { - testSubject, err := NewTemplateBuilder(BasicTemplate) - if err != nil { - t.Fatal(err) - } - - err = testSubject.BuildLinux("--test-ssh-authorized-key--", true) - if err != nil { - t.Fatal(err) - } - - err = testSubject.SetMarketPlaceImage("Canonical", "UbuntuServer", "16.04", "latest", compute.CachingTypesReadWrite) - if err != nil { - t.Fatal(err) - } - - doc, err := testSubject.ToJSON() - if err != nil { - t.Fatal(err) - } - - err = approvaltests.VerifyJSONBytes(t, []byte(*doc)) - if err != nil { - t.Fatal(err) - } -} - -// Ensure that a user can specify a custom VHD when building a Linux template. -func TestBuildLinux01(t *testing.T) { - testSubject, err := NewTemplateBuilder(BasicTemplate) - if err != nil { - t.Fatal(err) - } - - err = testSubject.BuildLinux("--test-ssh-authorized-key--", false) - if err != nil { - t.Fatal(err) - } - - err = testSubject.SetImageUrl("http://azure/custom.vhd", compute.Linux, compute.CachingTypesReadWrite) - if err != nil { - t.Fatal(err) - } - - doc, err := testSubject.ToJSON() - if err != nil { - t.Fatal(err) - } - - err = approvaltests.VerifyJSONBytes(t, []byte(*doc)) - if err != nil { - t.Fatal(err) - } -} - -// Ensure that a user can specify an existing Virtual Network -func TestBuildLinux02(t *testing.T) { - testSubject, err := NewTemplateBuilder(BasicTemplate) - if err != nil { - t.Fatal(err) - } - - err = testSubject.BuildLinux("--test-ssh-authorized-key--", true) - if err != nil { - t.Fatal(err) - } - err = testSubject.SetImageUrl("http://azure/custom.vhd", compute.Linux, compute.CachingTypesReadWrite) - if err != nil { - t.Fatal(err) - } - err = testSubject.SetOSDiskSizeGB(100) - if err != nil { - t.Fatal(err) - } - - err = testSubject.SetVirtualNetwork("--virtual-network-resource-group--", "--virtual-network--", "--subnet-name--") - if err != nil { - t.Fatal(err) - } - - doc, err := testSubject.ToJSON() - if err != nil { - t.Fatal(err) - } - - err = approvaltests.VerifyJSONBytes(t, []byte(*doc)) - if err != nil { - t.Fatal(err) - } -} - -// Ensure that a Windows template is configured as expected. -// * Include WinRM configuration. -// * Include KeyVault configuration, which is needed for WinRM. -func TestBuildWindows00(t *testing.T) { - testSubject, err := NewTemplateBuilder(BasicTemplate) - if err != nil { - t.Fatal(err) - } - - err = testSubject.BuildWindows("--test-key-vault-name", "--test-winrm-certificate-url--") - if err != nil { - t.Fatal(err) - } - - err = testSubject.SetMarketPlaceImage("MicrosoftWindowsServer", "WindowsServer", "2012-R2-Datacenter", "latest", compute.CachingTypesReadWrite) - if err != nil { - t.Fatal(err) - } - - doc, err := testSubject.ToJSON() - if err != nil { - t.Fatal(err) - } - - err = approvaltests.VerifyJSONBytes(t, []byte(*doc)) - if err != nil { - t.Fatal(err) - } -} - -// Windows build with additional disk for an managed build -func TestBuildWindows01(t *testing.T) { - testSubject, err := NewTemplateBuilder(BasicTemplate) - if err != nil { - t.Fatal(err) - } - - err = testSubject.BuildWindows("--test-key-vault-name", "--test-winrm-certificate-url--") - if err != nil { - t.Fatal(err) - } - - err = testSubject.SetManagedMarketplaceImage("MicrosoftWindowsServer", "WindowsServer", "2012-R2-Datacenter", "latest", "2015-1", "1", "Premium_LRS", compute.CachingTypesReadWrite) - if err != nil { - t.Fatal(err) - } - - err = testSubject.SetAdditionalDisks([]int32{32, 64}, "datadisk", true, compute.CachingTypesReadWrite) - if err != nil { - t.Fatal(err) - } - - doc, err := testSubject.ToJSON() - if err != nil { - t.Fatal(err) - } - - err = approvaltests.VerifyJSONBytes(t, []byte(*doc)) - if err != nil { - t.Fatal(err) - } -} - -// Windows build with additional disk for an unmanaged build -func TestBuildWindows02(t *testing.T) { - testSubject, err := NewTemplateBuilder(BasicTemplate) - if err != nil { - t.Fatal(err) - } - - err = testSubject.BuildWindows("--test-key-vault-name", "--test-winrm-certificate-url--") - if err != nil { - t.Fatal(err) - } - - err = testSubject.SetAdditionalDisks([]int32{32, 64}, "datadisk", false, compute.CachingTypesReadWrite) - if err != nil { - t.Fatal(err) - } - - doc, err := testSubject.ToJSON() - if err != nil { - t.Fatal(err) - } - - err = approvaltests.VerifyJSONBytes(t, []byte(*doc)) - if err != nil { - t.Fatal(err) - } -} - -// Shared Image Gallery Build -func TestSharedImageGallery00(t *testing.T) { - testSubject, err := NewTemplateBuilder(BasicTemplate) - if err != nil { - t.Fatal(err) - } - - err = testSubject.BuildLinux("--test-ssh-authorized-key--", false) - if err != nil { - t.Fatal(err) - } - - imageID := "/subscriptions/ignore/resourceGroups/ignore/providers/Microsoft.Compute/galleries/ignore/images/ignore" - err = testSubject.SetSharedGalleryImage("westcentralus", imageID, compute.CachingTypesReadOnly) - if err != nil { - t.Fatal(err) - } - - doc, err := testSubject.ToJSON() - if err != nil { - t.Fatal(err) - } - - err = approvaltests.VerifyJSONBytes(t, []byte(*doc)) - if err != nil { - t.Fatal(err) - } -} - -// Linux build with Network Security Group -func TestNetworkSecurityGroup00(t *testing.T) { - testSubject, err := NewTemplateBuilder(BasicTemplate) - if err != nil { - t.Fatal(err) - } - - err = testSubject.BuildLinux("--test-ssh-authorized-key--", false) - if err != nil { - t.Fatal(err) - } - - err = testSubject.SetMarketPlaceImage("Canonical", "UbuntuServer", "16.04", "latest", compute.CachingTypesReadWrite) - if err != nil { - t.Fatal(err) - } - - err = testSubject.SetNetworkSecurityGroup([]string{"127.0.0.1", "192.168.100.0/24"}, 123) - if err != nil { - t.Fatal(err) - } - - doc, err := testSubject.ToJSON() - if err != nil { - t.Fatal(err) - } - - err = approvaltests.VerifyJSONBytes(t, []byte(*doc)) - if err != nil { - t.Fatal(err) - } -} - -// Linux with user assigned managed identity configured -func TestSetIdentity00(t *testing.T) { - testSubject, err := NewTemplateBuilder(BasicTemplate) - if err != nil { - t.Fatal(err) - } - - if err = testSubject.BuildLinux("--test-ssh-authorized-key--", true); err != nil { - t.Fatal(err) - } - - if err = testSubject.SetMarketPlaceImage("Canonical", "UbuntuServer", "16.04", "latest", compute.CachingTypesReadWrite); err != nil { - t.Fatal(err) - } - - if err = testSubject.SetIdentity([]string{"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg1/providers/Microsoft.ManagedIdentity/userAssignedIdentities/id"}); err != nil { - t.Fatal(err) - } - - doc, err := testSubject.ToJSON() - if err != nil { - t.Fatal(err) - } - - if err = approvaltests.VerifyJSONBytes(t, []byte(*doc)); err != nil { - t.Fatal(err) - } -} diff --git a/builder/azure/common/template/template_parameters.go b/builder/azure/common/template/template_parameters.go deleted file mode 100644 index 262c85e56..000000000 --- a/builder/azure/common/template/template_parameters.go +++ /dev/null @@ -1,39 +0,0 @@ -package template - -// The intent of these types to facilitate interchange with Azure in the -// appropriate JSON format. A sample format is below. Each parameter listed -// below corresponds to a parameter defined in the template. -// -// { -// "storageAccountName": { -// "value" : "my_storage_account_name" -// }, -// "adminUserName" : { -// "value": "admin" -// } -// } - -type TemplateParameter struct { - Value string `json:"value"` -} - -type TemplateParameters struct { - AdminUsername *TemplateParameter `json:"adminUsername,omitempty"` - AdminPassword *TemplateParameter `json:"adminPassword,omitempty"` - DnsNameForPublicIP *TemplateParameter `json:"dnsNameForPublicIP,omitempty"` - KeyVaultName *TemplateParameter `json:"keyVaultName,omitempty"` - KeyVaultSKU *TemplateParameter `json:"keyVaultSKU,omitempty"` - KeyVaultSecretValue *TemplateParameter `json:"keyVaultSecretValue,omitempty"` - ObjectId *TemplateParameter `json:"objectId,omitempty"` - NicName *TemplateParameter `json:"nicName,omitempty"` - OSDiskName *TemplateParameter `json:"osDiskName,omitempty"` - DataDiskName *TemplateParameter `json:"dataDiskName,omitempty"` - PublicIPAddressName *TemplateParameter `json:"publicIPAddressName,omitempty"` - StorageAccountBlobEndpoint *TemplateParameter `json:"storageAccountBlobEndpoint,omitempty"` - SubnetName *TemplateParameter `json:"subnetName,omitempty"` - TenantId *TemplateParameter `json:"tenantId,omitempty"` - VirtualNetworkName *TemplateParameter `json:"virtualNetworkName,omitempty"` - NsgName *TemplateParameter `json:"nsgName,omitempty"` - VMSize *TemplateParameter `json:"vmSize,omitempty"` - VMName *TemplateParameter `json:"vmName,omitempty"` -} diff --git a/builder/azure/common/template/template_parameters_test.go b/builder/azure/common/template/template_parameters_test.go deleted file mode 100644 index 7a321128f..000000000 --- a/builder/azure/common/template/template_parameters_test.go +++ /dev/null @@ -1,112 +0,0 @@ -package template - -import ( - "encoding/json" - "fmt" - "strings" - "testing" -) - -func TestTemplateParametersShouldHaveExpectedKeys(t *testing.T) { - params := TemplateParameters{ - AdminUsername: &TemplateParameter{Value: "sentinel"}, - AdminPassword: &TemplateParameter{Value: "sentinel"}, - DnsNameForPublicIP: &TemplateParameter{Value: "sentinel"}, - OSDiskName: &TemplateParameter{Value: "sentinel"}, - StorageAccountBlobEndpoint: &TemplateParameter{Value: "sentinel"}, - VMName: &TemplateParameter{Value: "sentinel"}, - VMSize: &TemplateParameter{Value: "sentinel"}, - NsgName: &TemplateParameter{Value: "sentinel"}, - } - - bs, err := json.Marshal(params) - if err != nil { - t.Fail() - } - - var doc map[string]*json.RawMessage - err = json.Unmarshal(bs, &doc) - - if err != nil { - t.Fail() - } - - expectedKeys := []string{ - "adminUsername", - "adminPassword", - "dnsNameForPublicIP", - "osDiskName", - "storageAccountBlobEndpoint", - "vmSize", - "vmName", - "nsgName", - } - - for _, expectedKey := range expectedKeys { - _, containsKey := doc[expectedKey] - if containsKey == false { - t.Fatalf("Expected template parameters to contain the key value '%s', but it did not!", expectedKey) - } - } -} - -func TestParameterValuesShouldBeSet(t *testing.T) { - params := TemplateParameters{ - AdminUsername: &TemplateParameter{Value: "adminusername00"}, - AdminPassword: &TemplateParameter{Value: "adminpassword00"}, - DnsNameForPublicIP: &TemplateParameter{Value: "dnsnameforpublicip00"}, - OSDiskName: &TemplateParameter{Value: "osdiskname00"}, - StorageAccountBlobEndpoint: &TemplateParameter{Value: "storageaccountblobendpoint00"}, - VMName: &TemplateParameter{Value: "vmname00"}, - VMSize: &TemplateParameter{Value: "vmsize00"}, - NsgName: &TemplateParameter{Value: "nsgname00"}, - } - - bs, err := json.Marshal(params) - if err != nil { - t.Fail() - } - - var doc map[string]map[string]interface{} - err = json.Unmarshal(bs, &doc) - - if err != nil { - t.Fail() - } - - for k, v := range doc { - var expectedValue = fmt.Sprintf("%s00", strings.ToLower(k)) - var actualValue, exists = v["value"] - if exists != true { - t.Errorf("Expected to find a 'value' key under '%s', but it was missing!", k) - } - - if expectedValue != actualValue { - t.Errorf("Expected '%s', but actual was '%s'!", expectedValue, actualValue) - } - } -} - -func TestEmptyValuesShouldBeOmitted(t *testing.T) { - params := TemplateParameters{ - AdminUsername: &TemplateParameter{Value: "adminusername00"}, - } - - bs, err := json.Marshal(params) - if err != nil { - t.Fail() - } - - var doc map[string]map[string]interface{} - err = json.Unmarshal(bs, &doc) - - if err != nil { - t.Fail() - } - - if len(doc) != 1 { - t.Errorf("Failed to omit empty template parameters from the JSON document!") - t.Errorf("doc=%+v", doc) - t.Fail() - } -} diff --git a/builder/azure/common/template_funcs.go b/builder/azure/common/template_funcs.go deleted file mode 100644 index faccd4f07..000000000 --- a/builder/azure/common/template_funcs.go +++ /dev/null @@ -1,40 +0,0 @@ -package common - -import ( - "bytes" - "text/template" -) - -func isValidByteValue(b byte) bool { - if '0' <= b && b <= '9' { - return true - } - if 'a' <= b && b <= 'z' { - return true - } - if 'A' <= b && b <= 'Z' { - return true - } - return b == '.' || b == '_' || b == '-' -} - -// Clean up image name by replacing invalid characters with "-" -// Names are not allowed to end in '.', '-', or '_' and are trimmed. -func templateCleanImageName(s string) string { - b := []byte(s) - newb := make([]byte, len(b)) - for i := range newb { - if isValidByteValue(b[i]) { - newb[i] = b[i] - } else { - newb[i] = '-' - } - } - - newb = bytes.TrimRight(newb, "-_.") - return string(newb) -} - -var TemplateFuncs = template.FuncMap{ - "clean_resource_name": templateCleanImageName, -} diff --git a/builder/azure/common/template_funcs_test.go b/builder/azure/common/template_funcs_test.go deleted file mode 100644 index 1bdcf303f..000000000 --- a/builder/azure/common/template_funcs_test.go +++ /dev/null @@ -1,49 +0,0 @@ -package common - -import "testing" - -func TestTemplateCleanImageName(t *testing.T) { - vals := []struct { - origName string - expected string - }{ - // test that valid name is unchanged - { - origName: "abcde-012345xyz", - expected: "abcde-012345xyz", - }, - // test that colons are converted to hyphens - { - origName: "abcde-012345v1.0:0", - expected: "abcde-012345v1.0-0", - }, - // Name starting with number is not valid, but not in scope of this - // function to correct - { - origName: "012345v1.0:0", - expected: "012345v1.0-0", - }, - // Name over 80 chars is not valid, but not corrected by this function. - { - origName: "l012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789", - expected: "l012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789", - }, - // Name cannot end in a -Name over 80 chars is not valid, but not corrected by this function. - { - origName: "abcde-:_", - expected: "abcde", - }, - // Lost of special characters - { - origName: "My()./-_:&^ $%[]#'@name", - expected: "My--.--_-----------name", - }, - } - - for _, v := range vals { - name := templateCleanImageName(v.origName) - if name != v.expected { - t.Fatalf("template names do not match: expected %s got %s\n", v.expected, name) - } - } -} diff --git a/builder/azure/common/vault.go b/builder/azure/common/vault.go deleted file mode 100644 index a1732d8f5..000000000 --- a/builder/azure/common/vault.go +++ /dev/null @@ -1,138 +0,0 @@ -// NOTE: vault APIs do not yet exist in the SDK, but once they do this code -// should be removed. - -package common - -import ( - "fmt" - "net/http" - "net/url" - - "github.com/Azure/go-autorest/autorest" -) - -const ( - AzureVaultApiVersion = "2016-10-01" -) - -// Enables us to test steps that access this cli -type AZVaultClientIface interface { - GetSecret(string, string) (*Secret, error) - SetSecret(string, string, string) error -} - -type VaultClient struct { - autorest.Client - keyVaultEndpoint url.URL - SubscriptionID string - baseURI string -} - -func NewVaultClient(keyVaultEndpoint url.URL) VaultClient { - return VaultClient{ - keyVaultEndpoint: keyVaultEndpoint, - } -} - -func NewVaultClientWithBaseURI(baseURI, subscriptionID string) VaultClient { - return VaultClient{ - baseURI: baseURI, - SubscriptionID: subscriptionID, - } -} - -type Secret struct { - ID *string `json:"id,omitempty"` - Value string `json:"value"` -} - -func (client *VaultClient) GetSecret(vaultName, secretName string) (*Secret, error) { - p := map[string]interface{}{ - "secret-name": autorest.Encode("path", secretName), - } - q := map[string]interface{}{ - "api-version": AzureVaultApiVersion, - } - - req, err := autorest.Prepare( - &http.Request{}, - autorest.AsGet(), - autorest.WithBaseURL(client.getVaultUrl(vaultName)), - autorest.WithPathParameters("/secrets/{secret-name}", p), - autorest.WithQueryParameters(q), - ) - - if err != nil { - return nil, err - } - - resp, err := autorest.SendWithSender(client, req) - if err != nil { - return nil, err - } - - if resp.StatusCode != 200 { - return nil, fmt.Errorf( - "Failed to fetch secret from %s/%s, HTTP status code=%d (%s)", - vaultName, - secretName, - resp.StatusCode, - http.StatusText(resp.StatusCode)) - } - - var secret Secret - - err = autorest.Respond( - resp, - autorest.ByUnmarshallingJSON(&secret)) - if err != nil { - return nil, err - } - - return &secret, nil -} - -func (client *VaultClient) SetSecret(vaultName, secretName string, secretValue string) error { - p := map[string]interface{}{ - "secret-name": autorest.Encode("path", secretName), - } - q := map[string]interface{}{ - "api-version": AzureVaultApiVersion, - } - - jsonBody := fmt.Sprintf(`{"value": "%s"}`, secretValue) - - req, err := autorest.Prepare( - &http.Request{}, - autorest.AsPut(), - autorest.AsContentType("application/json; charset=utf-8"), - autorest.WithBaseURL(client.getVaultUrl(vaultName)), - autorest.WithPathParameters("/secrets/{secret-name}", p), - autorest.WithQueryParameters(q), - autorest.WithString(jsonBody), - ) - - if err != nil { - return err - } - - resp, err := autorest.SendWithSender(client, req) - if err != nil { - return err - } - - if resp.StatusCode != 200 { - return fmt.Errorf( - "Failed to set secret to %s/%s, HTTP status code=%d (%s)", - vaultName, - secretName, - resp.StatusCode, - http.StatusText(resp.StatusCode)) - } - - return nil -} - -func (client *VaultClient) getVaultUrl(vaultName string) string { - return fmt.Sprintf("%s://%s.%s/", client.keyVaultEndpoint.Scheme, vaultName, client.keyVaultEndpoint.Host) -} diff --git a/builder/azure/common/vault_client_mock.go b/builder/azure/common/vault_client_mock.go deleted file mode 100644 index 57bbd1c11..000000000 --- a/builder/azure/common/vault_client_mock.go +++ /dev/null @@ -1,56 +0,0 @@ -package common - -import ( - "fmt" - "net/http" - - "github.com/Azure/go-autorest/autorest" -) - -type MockAZVaultClient struct { - GetSecretCalled bool - SetSecretCalled bool - SetSecretVaultName string - SetSecretSecretName string - SetSecretCert string - DeleteResponderCalled bool - DeletePreparerCalled bool - DeleteSenderCalled bool - - IsError bool -} - -func (m *MockAZVaultClient) GetSecret(vaultName, secretName string) (*Secret, error) { - m.GetSecretCalled = true - var secret Secret - return &secret, nil -} - -func (m *MockAZVaultClient) SetSecret(vaultName, secretName string, secretValue string) error { - m.SetSecretCalled = true - m.SetSecretVaultName = vaultName - m.SetSecretSecretName = secretName - m.SetSecretCert = secretValue - - if m.IsError { - return fmt.Errorf("generic error!!") - } - - return nil -} - -func (m *MockAZVaultClient) DeletePreparer(resourceGroupName string, vaultName string) (*http.Request, error) { - m.DeletePreparerCalled = true - return nil, nil -} - -func (m *MockAZVaultClient) DeleteResponder(resp *http.Response) (autorest.Response, error) { - m.DeleteResponderCalled = true - var result autorest.Response - return result, nil -} - -func (m *MockAZVaultClient) DeleteSender(req *http.Request) (*http.Response, error) { - m.DeleteSenderCalled = true - return nil, nil -} diff --git a/builder/azure/common/vault_test.go b/builder/azure/common/vault_test.go deleted file mode 100644 index d1c94d76c..000000000 --- a/builder/azure/common/vault_test.go +++ /dev/null @@ -1,26 +0,0 @@ -package common - -import ( - "net/url" - "testing" -) - -func TestVaultClientKeyVaultEndpoint(t *testing.T) { - u, _ := url.Parse("https://vault.azure.net") - testSubject := NewVaultClient(*u) - - vaultUrl := testSubject.getVaultUrl("my") - if vaultUrl != "https://my.vault.azure.net/" { - t.Errorf("expected \"https://my.vault.azure.net/\", got %q", vaultUrl) - } -} - -func TestVaultClientKeyVaultEndpointPreserveScheme(t *testing.T) { - u, _ := url.Parse("http://vault.azure.net") - testSubject := NewVaultClient(*u) - - vaultUrl := testSubject.getVaultUrl("my") - if vaultUrl != "http://my.vault.azure.net/" { - t.Errorf("expected \"http://my.vault.azure.net/\", got %q", vaultUrl) - } -} diff --git a/builder/azure/dtl/TestVirtualMachineDeployment05.approved.txt b/builder/azure/dtl/TestVirtualMachineDeployment05.approved.txt deleted file mode 100644 index 31532ddbe..000000000 --- a/builder/azure/dtl/TestVirtualMachineDeployment05.approved.txt +++ /dev/null @@ -1,118 +0,0 @@ -{ - "$schema": "http://schema.management.azure.com/schemas/2014-04-01-preview/deploymentTemplate.json", - "contentVersion": "1.0.0.0", - "parameters": { - "adminPassword": { - "type": "string" - }, - "adminUsername": { - "type": "string" - }, - "dnsNameForPublicIP": { - "type": "string" - }, - "osDiskName": { - "type": "string" - }, - "storageAccountBlobEndpoint": { - "type": "string" - }, - "vmName": { - "type": "string" - }, - "vmSize": { - "type": "string" - } - }, - "resources": [ - { - "apiVersion": "[variables('apiVersion')]", - "dependsOn": [], - "location": "[variables('location')]", - "name": "[variables('nicName')]", - "properties": { - "ipConfigurations": [ - { - "name": "ipconfig", - "properties": { - "privateIPAllocationMethod": "Dynamic", - "subnet": { - "id": "[variables('subnetRef')]" - } - } - } - ] - }, - "type": "Microsoft.Network/networkInterfaces" - }, - { - "apiVersion": "[variables('apiVersion')]", - "dependsOn": [ - "[concat('Microsoft.Network/networkInterfaces/', variables('nicName'))]" - ], - "location": "[variables('location')]", - "name": "[parameters('vmName')]", - "properties": { - "diagnosticsProfile": { - "bootDiagnostics": { - "enabled": false - } - }, - "hardwareProfile": { - "vmSize": "[parameters('vmSize')]" - }, - "networkProfile": { - "networkInterfaces": [ - { - "id": "[resourceId('Microsoft.Network/networkInterfaces', variables('nicName'))]" - } - ] - }, - "osProfile": { - "adminPassword": "[parameters('adminPassword')]", - "adminUsername": "[parameters('adminUsername')]", - "computerName": "[parameters('vmName')]", - "linuxConfiguration": { - "ssh": { - "publicKeys": [ - { - "keyData": "", - "path": "[variables('sshKeyPath')]" - } - ] - } - } - }, - "storageProfile": { - "osDisk": { - "caching": "ReadWrite", - "createOption": "FromImage", - "image": { - "uri": "https://localhost/custom.vhd" - }, - "name": "[parameters('osDiskName')]", - "osType": "Linux", - "vhd": { - "uri": "[concat(parameters('storageAccountBlobEndpoint'),variables('vmStorageAccountContainerName'),'/', parameters('osDiskName'),'.vhd')]" - } - } - } - }, - "type": "Microsoft.Compute/virtualMachines" - } - ], - "variables": { - "addressPrefix": "10.0.0.0/16", - "apiVersion": "2015-06-15", - "location": "[resourceGroup().location]", - "publicIPAddressType": "Dynamic", - "sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]", - "subnetAddressPrefix": "10.0.0.0/24", - "subnetName": "ignore", - "subnetRef": "[concat(variables('vnetID'),'/subnets/',variables('subnetName'))]", - "virtualNetworkName": "ignore", - "virtualNetworkResourceGroup": "ignore", - "vmStorageAccountContainerName": "images", - "vnetID": "[resourceId(variables('virtualNetworkResourceGroup'), 'Microsoft.Network/virtualNetworks', variables('virtualNetworkName'))]" - } -} \ No newline at end of file diff --git a/builder/azure/dtl/WindowsMixAndMatch.json b/builder/azure/dtl/WindowsMixAndMatch.json deleted file mode 100644 index 1457d4a48..000000000 --- a/builder/azure/dtl/WindowsMixAndMatch.json +++ /dev/null @@ -1,65 +0,0 @@ -{ - "variables": { - "client_id": "{{env `client_id`}}", - "client_secret": "{{env `client_secret`}}", - "tenant_id": "{{env `tenant_id`}}", - "subscription_id": "{{env `subscription_id`}}", - "lab_name": "NewpackerRG", - "resource_group_name": "NewPackerRG", - "vm_name": "win21" - }, - "builders": [ - { - "type": "azure-dtl", - "client_id": "{{user `client_id`}}", - "client_secret": "{{user `client_secret`}}", - "tenant_id": "{{user `tenant_id`}}", - "subscription_id": "{{user `subscription_id`}}", - "managed_image_resource_group_name": "NewPackerRG", - "managed_image_name": "PackerImage2", - "os_type": "Windows", - "image_publisher": "MicrosoftWindowsDesktop", - "image_offer": "Windows-10", - "image_sku": "19h1-ent", - "azure_tags": { - "dept": "Engineering", - "task": "Image deployment" - }, - "dtl_artifacts": [ - { - "artifact_name": "windows-7zip" - }, - { - "artifact_name": "windows-mongodb" - } - ], - "lab_name": "{{user `lab_name`}}", - "vm_name": "{{user `vm_name`}}", - "lab_virtual_network_name": "dtlnewpackerrg", - "lab_resource_group_name": "{{user `resource_group_name`}}", - "lab_subnet_name": "dtlnewpackerrgSubnet", - "location": "Central US", - "vm_size": "Standard_DS2_v2" - } - ], - "provisioners": [ - { - "type": "azure-dtlartifact", - "client_id": "{{user `client_id`}}", - "client_secret": "{{user `client_secret`}}", - "tenant_id": "{{user `tenant_id`}}", - "subscription_id": "{{user `subscription_id`}}", - "lab_name": "{{user `lab_name`}}", - "resource_group_name": "{{user `resource_group_name`}}", - "vm_name": "{{user `vm_name`}}", - "dtl_artifacts": [ - { - "artifact_name": "windows-chrome" - }, - { - "artifact_name": "windows-azurepowershell" - } - ] - } - ] -} \ No newline at end of file diff --git a/builder/azure/dtl/WindowsSimple.json b/builder/azure/dtl/WindowsSimple.json deleted file mode 100644 index 446c41c33..000000000 --- a/builder/azure/dtl/WindowsSimple.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "variables": { - "client_id": "{{env `client_id`}}", - "client_secret": "{{env `client_secret`}}", - "tenant_id": "{{env `tenant_id`}}", - "subscription_id": "{{env `subscription_id`}}", - "lab_name": "NewpackerRG", - "resource_group_name": "NewPackerRG", - "vm_name": "win18" - }, - "builders": [ - { - "type": "azure-dtl", - "client_id": "{{user `client_id`}}", - "client_secret": "{{user `client_secret`}}", - "tenant_id": "{{user `tenant_id`}}", - "subscription_id": "{{user `subscription_id`}}", - "managed_image_resource_group_name": "NewPackerRG", - "managed_image_name": "PackerImage2", - "os_type": "Windows", - "image_publisher": "MicrosoftWindowsDesktop", - "image_offer": "Windows-10", - "image_sku": "19h1-ent", - "azure_tags": { - "dept": "Engineering", - "task": "Image deployment" - }, - "lab_name": "{{user `lab_name`}}", - "vm_name": "{{user `vm_name`}}", - "lab_virtual_network_name": "dtlnewpackerrg", - "lab_resource_group_name": "{{user `resource_group_name`}}", - "lab_subnet_name": "dtlnewpackerrgSubnet", - "location": "Central US", - "vm_size": "Standard_DS2_v2" - } - ] -} \ No newline at end of file diff --git a/builder/azure/dtl/acceptancetest.json b/builder/azure/dtl/acceptancetest.json deleted file mode 100644 index d1618e00d..000000000 --- a/builder/azure/dtl/acceptancetest.json +++ /dev/null @@ -1,72 +0,0 @@ -{ - "$schema": "http://schema.management.azure.com/schemas/2014-04-01-preview/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "parameters": { - "name": { - "type": "string", - "defaultValue": "packer-acceptance-test" - }, - "regionId": { - "type": "string", - "defaultValue": "southcentralus" - } - }, - "resources": [ - { - "apiVersion": "2018-10-15-preview", - "name": "[parameters('name')]", - "type": "Microsoft.DevTestLab/labs", - "location": "[parameters('regionId')]", - "tags": { - "env": "packer" - }, - "properties": { - "labStorageType": "Premium" - }, - "resources": [ - { - "apiVersion": "2018-10-15-preview", - "name": "LabVmsShutdown", - "location": "[parameters('regionId')]", - "type": "schedules", - "dependsOn": [ - "[resourceId('Microsoft.DevTestLab/labs', parameters('name'))]" - ], - "properties": { - "status": "Enabled", - "timeZoneId": "Pacific Standard Time", - "dailyRecurrence": { - "time": "1900" - }, - "taskType": "LabVmsShutdownTask", - "notificationSettings": { - "status": "Disabled", - "timeInMinutes": 30 - } - } - }, - { - "apiVersion": "2018-10-15-preview", - "name": "[concat('Dtl', parameters('name'))]", - "type": "virtualNetworks", - "location": "[parameters('regionId')]", - "dependsOn": [ - "[resourceId('Microsoft.DevTestLab/labs', parameters('name'))]" - ] - }, - { - "apiVersion": "2018-10-15-preview", - "name": "Public Environment Repo", - "type": "artifactSources", - "location": "[parameters('regionId')]", - "dependsOn": [ - "[resourceId('Microsoft.DevTestLab/labs', parameters('name'))]" - ], - "properties": { - "status": "Enabled" - } - } - ] - } - ] -} \ No newline at end of file diff --git a/builder/azure/dtl/artifact.go b/builder/azure/dtl/artifact.go deleted file mode 100644 index 0035e2d00..000000000 --- a/builder/azure/dtl/artifact.go +++ /dev/null @@ -1,199 +0,0 @@ -package dtl - -import ( - "bytes" - "fmt" - "net/url" - "path" - "strings" -) - -const ( - BuilderId = "Azure.ResourceManagement.VMImage" -) - -type AdditionalDiskArtifact struct { - AdditionalDiskUri string - AdditionalDiskUriReadOnlySas string -} - -type Artifact struct { - // OS type: Linux, Windows - OSType string - - // VHD - StorageAccountLocation string - OSDiskUri string - TemplateUri string - OSDiskUriReadOnlySas string - TemplateUriReadOnlySas string - - // Managed Image - ManagedImageResourceGroupName string - ManagedImageName string - ManagedImageLocation string - ManagedImageId string - ManagedImageOSDiskSnapshotName string - ManagedImageDataDiskSnapshotPrefix string - - // Additional Disks - AdditionalDisks *[]AdditionalDiskArtifact -} - -func NewManagedImageArtifact(osType, resourceGroup, name, location, id string) (*Artifact, error) { - return &Artifact{ - ManagedImageResourceGroupName: resourceGroup, - ManagedImageName: name, - ManagedImageLocation: location, - ManagedImageId: id, - OSType: osType, - }, nil -} - -func NewArtifact(template *CaptureTemplate, getSasUrl func(name string) string, osType string) (*Artifact, error) { - if template == nil { - return nil, fmt.Errorf("nil capture template") - } - - if len(template.Resources) != 1 { - return nil, fmt.Errorf("malformed capture template, expected one resource") - } - - vhdUri, err := url.Parse(template.Resources[0].Properties.StorageProfile.OSDisk.Image.Uri) - if err != nil { - return nil, err - } - - templateUri, err := storageUriToTemplateUri(vhdUri) - if err != nil { - return nil, err - } - - var additional_disks *[]AdditionalDiskArtifact - if template.Resources[0].Properties.StorageProfile.DataDisks != nil { - data_disks := make([]AdditionalDiskArtifact, len(template.Resources[0].Properties.StorageProfile.DataDisks)) - for i, additionaldisk := range template.Resources[0].Properties.StorageProfile.DataDisks { - additionalVhdUri, err := url.Parse(additionaldisk.Image.Uri) - if err != nil { - return nil, err - } - data_disks[i].AdditionalDiskUri = additionalVhdUri.String() - data_disks[i].AdditionalDiskUriReadOnlySas = getSasUrl(getStorageUrlPath(additionalVhdUri)) - } - additional_disks = &data_disks - } - - return &Artifact{ - OSType: osType, - OSDiskUri: vhdUri.String(), - OSDiskUriReadOnlySas: getSasUrl(getStorageUrlPath(vhdUri)), - TemplateUri: templateUri.String(), - TemplateUriReadOnlySas: getSasUrl(getStorageUrlPath(templateUri)), - - AdditionalDisks: additional_disks, - - StorageAccountLocation: template.Resources[0].Location, - }, nil -} - -func getStorageUrlPath(u *url.URL) string { - parts := strings.Split(u.Path, "/") - return strings.Join(parts[3:], "/") -} - -func storageUriToTemplateUri(su *url.URL) (*url.URL, error) { - // packer-osDisk.4085bb15-3644-4641-b9cd-f575918640b4.vhd -> 4085bb15-3644-4641-b9cd-f575918640b4 - filename := path.Base(su.Path) - parts := strings.Split(filename, ".") - - if len(parts) < 3 { - return nil, fmt.Errorf("malformed URL") - } - - // packer-osDisk.4085bb15-3644-4641-b9cd-f575918640b4.vhd -> packer - prefixParts := strings.Split(parts[0], "-") - prefix := strings.Join(prefixParts[:len(prefixParts)-1], "-") - - templateFilename := fmt.Sprintf("%s-vmTemplate.%s.json", prefix, parts[1]) - - // https://storage.blob.core.windows.net/system/Microsoft.Compute/Images/images/packer-osDisk.4085bb15-3644-4641-b9cd-f575918640b4.vhd" - // -> - // https://storage.blob.core.windows.net/system/Microsoft.Compute/Images/images/packer-vmTemplate.4085bb15-3644-4641-b9cd-f575918640b4.json" - return url.Parse(strings.Replace(su.String(), filename, templateFilename, 1)) -} - -func (a *Artifact) isManagedImage() bool { - return a.ManagedImageResourceGroupName != "" -} - -func (*Artifact) BuilderId() string { - return BuilderId -} - -func (*Artifact) Files() []string { - return []string{} -} - -func (a *Artifact) Id() string { - if a.OSDiskUri != "" { - return a.OSDiskUri - } - return a.ManagedImageId -} - -func (a *Artifact) State(name string) interface{} { - switch name { - case "atlas.artifact.metadata": - return a.stateAtlasMetadata() - default: - return nil - } -} - -func (a *Artifact) String() string { - var buf bytes.Buffer - - buf.WriteString(fmt.Sprintf("%s:\n\n", a.BuilderId())) - buf.WriteString(fmt.Sprintf("OSType: %s\n", a.OSType)) - if a.isManagedImage() { - buf.WriteString(fmt.Sprintf("ManagedImageResourceGroupName: %s\n", a.ManagedImageResourceGroupName)) - buf.WriteString(fmt.Sprintf("ManagedImageName: %s\n", a.ManagedImageName)) - buf.WriteString(fmt.Sprintf("ManagedImageId: %s\n", a.ManagedImageId)) - buf.WriteString(fmt.Sprintf("ManagedImageLocation: %s\n", a.ManagedImageLocation)) - if a.ManagedImageOSDiskSnapshotName != "" { - buf.WriteString(fmt.Sprintf("ManagedImageOSDiskSnapshotName: %s\n", a.ManagedImageOSDiskSnapshotName)) - } - if a.ManagedImageDataDiskSnapshotPrefix != "" { - buf.WriteString(fmt.Sprintf("ManagedImageDataDiskSnapshotPrefix: %s\n", a.ManagedImageDataDiskSnapshotPrefix)) - } - } else { - buf.WriteString(fmt.Sprintf("StorageAccountLocation: %s\n", a.StorageAccountLocation)) - buf.WriteString(fmt.Sprintf("OSDiskUri: %s\n", a.OSDiskUri)) - buf.WriteString(fmt.Sprintf("OSDiskUriReadOnlySas: %s\n", a.OSDiskUriReadOnlySas)) - buf.WriteString(fmt.Sprintf("TemplateUri: %s\n", a.TemplateUri)) - buf.WriteString(fmt.Sprintf("TemplateUriReadOnlySas: %s\n", a.TemplateUriReadOnlySas)) - if a.AdditionalDisks != nil { - for i, additionaldisk := range *a.AdditionalDisks { - buf.WriteString(fmt.Sprintf("AdditionalDiskUri (datadisk-%d): %s\n", i+1, additionaldisk.AdditionalDiskUri)) - buf.WriteString(fmt.Sprintf("AdditionalDiskUriReadOnlySas (datadisk-%d): %s\n", i+1, additionaldisk.AdditionalDiskUriReadOnlySas)) - } - } - } - - return buf.String() -} - -func (*Artifact) Destroy() error { - return nil -} - -func (a *Artifact) stateAtlasMetadata() interface{} { - metadata := make(map[string]string) - metadata["StorageAccountLocation"] = a.StorageAccountLocation - metadata["OSDiskUri"] = a.OSDiskUri - metadata["OSDiskUriReadOnlySas"] = a.OSDiskUriReadOnlySas - metadata["TemplateUri"] = a.TemplateUri - metadata["TemplateUriReadOnlySas"] = a.TemplateUriReadOnlySas - - return metadata -} diff --git a/builder/azure/dtl/azure_client.go b/builder/azure/dtl/azure_client.go deleted file mode 100644 index 286eaa00e..000000000 --- a/builder/azure/dtl/azure_client.go +++ /dev/null @@ -1,211 +0,0 @@ -package dtl - -import ( - "encoding/json" - "fmt" - "math" - "net/http" - "os" - "strconv" - "time" - - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2018-04-01/compute" - newCompute "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-03-01/compute" - dtl "github.com/Azure/azure-sdk-for-go/services/devtestlabs/mgmt/2018-09-15/dtl" - "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2018-01-01/network" - "github.com/Azure/azure-sdk-for-go/services/resources/mgmt/2018-02-01/resources" - 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" - "github.com/Azure/go-autorest/autorest/adal" - "github.com/Azure/go-autorest/autorest/azure" - "github.com/hashicorp/packer-plugin-sdk/useragent" - "github.com/hashicorp/packer/builder/azure/common" - "github.com/hashicorp/packer/builder/azure/version" -) - -const ( - EnvPackerLogAzureMaxLen = "PACKER_LOG_AZURE_MAXLEN" -) - -type AzureClient struct { - storage.BlobStorageClient - resources.DeploymentsClient - resources.DeploymentOperationsClient - resources.GroupsClient - network.PublicIPAddressesClient - network.InterfacesClient - network.SubnetsClient - network.VirtualNetworksClient - compute.ImagesClient - compute.VirtualMachinesClient - common.VaultClient - armStorage.AccountsClient - compute.DisksClient - compute.SnapshotsClient - newCompute.GalleryImageVersionsClient - newCompute.GalleryImagesClient - - InspectorMaxLength int - Template *CaptureTemplate - LastError azureErrorResponse - VaultClientDelete common.VaultClient - DtlLabsClient dtl.LabsClient - DtlVirtualMachineClient dtl.VirtualMachinesClient - DtlEnvironmentsClient dtl.EnvironmentsClient - DtlCustomImageClient dtl.CustomImagesClient - DtlVirtualNetworksClient dtl.VirtualNetworksClient -} - -func getCaptureResponse(body string) *CaptureTemplate { - var operation CaptureOperation - err := json.Unmarshal([]byte(body), &operation) - if err != nil { - return nil - } - - if operation.Properties != nil && operation.Properties.Output != nil { - return operation.Properties.Output - } - - return nil -} - -// HACK(chrboum): This method is a hack. It was written to work around this issue -// (https://github.com/Azure/azure-sdk-for-go/issues/307) and to an extent this -// issue (https://github.com/Azure/azure-rest-api-specs/issues/188). -// -// Capturing a VM is a long running operation that requires polling. There are -// couple different forms of polling, and the end result of a poll operation is -// discarded by the SDK. It is expected that any discarded data can be re-fetched, -// so discarding it has minimal impact. Unfortunately, there is no way to re-fetch -// the template returned by a capture call that I am aware of. -// -// If the second issue were fixed the VM ID would be included when GET'ing a VM. The -// VM ID could be used to locate the captured VHD, and captured template. -// Unfortunately, the VM ID is not included so this method cannot be used either. -// -// This code captures the template and saves it to the client (the AzureClient type). -// It expects that the capture API is called only once, or rather you only care that the -// last call's value is important because subsequent requests are not persisted. There -// is no care given to multiple threads writing this value because for our use case -// it does not matter. -func templateCapture(client *AzureClient) autorest.RespondDecorator { - return func(r autorest.Responder) autorest.Responder { - return autorest.ResponderFunc(func(resp *http.Response) error { - body, bodyString := handleBody(resp.Body, math.MaxInt64) - resp.Body = body - - captureTemplate := getCaptureResponse(bodyString) - if captureTemplate != nil { - client.Template = captureTemplate - } - - return r.Respond(resp) - }) - } -} - -func errorCapture(client *AzureClient) autorest.RespondDecorator { - return func(r autorest.Responder) autorest.Responder { - return autorest.ResponderFunc(func(resp *http.Response) error { - body, bodyString := handleBody(resp.Body, math.MaxInt64) - resp.Body = body - - errorResponse := newAzureErrorResponse(bodyString) - if errorResponse != nil { - client.LastError = *errorResponse - } - - return r.Respond(resp) - }) - } -} - -// WAITING(chrboum): I have logged https://github.com/Azure/azure-sdk-for-go/issues/311 to get this -// method included in the SDK. It has been accepted, and I'll cut over to the official way -// once it ships. -func byConcatDecorators(decorators ...autorest.RespondDecorator) autorest.RespondDecorator { - return func(r autorest.Responder) autorest.Responder { - return autorest.DecorateResponder(r, decorators...) - } -} - -func NewAzureClient(subscriptionID, resourceGroupName string, - cloud *azure.Environment, SharedGalleryTimeout time.Duration, PollingDuration time.Duration, - servicePrincipalToken *adal.ServicePrincipalToken) (*AzureClient, error) { - - var azureClient = &AzureClient{} - - maxlen := getInspectorMaxLength() - - azureClient.DtlVirtualMachineClient = dtl.NewVirtualMachinesClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID) - azureClient.DtlVirtualMachineClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken) - azureClient.DtlVirtualMachineClient.RequestInspector = withInspection(maxlen) - azureClient.DtlVirtualMachineClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), templateCapture(azureClient), errorCapture(azureClient)) - azureClient.DtlVirtualMachineClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(version.AzurePluginVersion.FormattedVersion()), azureClient.DtlVirtualMachineClient.UserAgent) - azureClient.DtlVirtualMachineClient.Client.PollingDuration = PollingDuration - - azureClient.DtlEnvironmentsClient = dtl.NewEnvironmentsClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID) - azureClient.DtlEnvironmentsClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken) - azureClient.DtlEnvironmentsClient.RequestInspector = withInspection(maxlen) - azureClient.DtlEnvironmentsClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), templateCapture(azureClient), errorCapture(azureClient)) - azureClient.DtlEnvironmentsClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(version.AzurePluginVersion.FormattedVersion()), azureClient.DtlEnvironmentsClient.UserAgent) - azureClient.DtlEnvironmentsClient.Client.PollingDuration = PollingDuration - - azureClient.DtlLabsClient = dtl.NewLabsClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID) - azureClient.DtlLabsClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken) - azureClient.DtlLabsClient.RequestInspector = withInspection(maxlen) - azureClient.DtlLabsClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), templateCapture(azureClient), errorCapture(azureClient)) - azureClient.DtlLabsClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(version.AzurePluginVersion.FormattedVersion()), azureClient.DtlLabsClient.UserAgent) - azureClient.DtlLabsClient.Client.PollingDuration = PollingDuration - - azureClient.DtlCustomImageClient = dtl.NewCustomImagesClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID) - azureClient.DtlCustomImageClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken) - azureClient.DtlCustomImageClient.RequestInspector = withInspection(maxlen) - azureClient.DtlCustomImageClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), templateCapture(azureClient), errorCapture(azureClient)) - azureClient.DtlCustomImageClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(version.AzurePluginVersion.FormattedVersion()), azureClient.DtlCustomImageClient.UserAgent) - azureClient.DtlCustomImageClient.PollingDuration = autorest.DefaultPollingDuration - azureClient.DtlCustomImageClient.Client.PollingDuration = PollingDuration - - azureClient.DtlVirtualNetworksClient = dtl.NewVirtualNetworksClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID) - azureClient.DtlVirtualNetworksClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken) - azureClient.DtlVirtualNetworksClient.RequestInspector = withInspection(maxlen) - azureClient.DtlVirtualNetworksClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), templateCapture(azureClient), errorCapture(azureClient)) - azureClient.DtlVirtualNetworksClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(version.AzurePluginVersion.FormattedVersion()), azureClient.DtlVirtualNetworksClient.UserAgent) - azureClient.DtlVirtualNetworksClient.Client.PollingDuration = PollingDuration - - azureClient.GalleryImageVersionsClient = newCompute.NewGalleryImageVersionsClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID) - azureClient.GalleryImageVersionsClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken) - azureClient.GalleryImageVersionsClient.RequestInspector = withInspection(maxlen) - azureClient.GalleryImageVersionsClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient)) - azureClient.GalleryImageVersionsClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(version.AzurePluginVersion.FormattedVersion()), azureClient.GalleryImageVersionsClient.UserAgent) - azureClient.GalleryImageVersionsClient.Client.PollingDuration = SharedGalleryTimeout - - azureClient.GalleryImagesClient = newCompute.NewGalleryImagesClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID) - azureClient.GalleryImagesClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken) - azureClient.GalleryImagesClient.RequestInspector = withInspection(maxlen) - azureClient.GalleryImagesClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient)) - azureClient.GalleryImagesClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(version.AzurePluginVersion.FormattedVersion()), azureClient.GalleryImagesClient.UserAgent) - azureClient.GalleryImagesClient.Client.PollingDuration = PollingDuration - - return azureClient, nil -} - -func getInspectorMaxLength() int64 { - value, ok := os.LookupEnv(EnvPackerLogAzureMaxLen) - if !ok { - return math.MaxInt64 - } - - i, err := strconv.ParseInt(value, 10, 64) - if err != nil { - return 0 - } - - if i < 0 { - return 0 - } - - return i -} diff --git a/builder/azure/dtl/azure_error_response.go b/builder/azure/dtl/azure_error_response.go deleted file mode 100644 index 2d92f101e..000000000 --- a/builder/azure/dtl/azure_error_response.go +++ /dev/null @@ -1,63 +0,0 @@ -package dtl - -import ( - "bytes" - "encoding/json" - "fmt" -) - -type azureErrorDetails struct { - Code string `json:"code"` - Message string `json:"message"` - Details []azureErrorDetails `json:"details"` -} - -type azureErrorResponse struct { - ErrorDetails azureErrorDetails `json:"error"` -} - -func newAzureErrorResponse(s string) *azureErrorResponse { - var errorResponse azureErrorResponse - err := json.Unmarshal([]byte(s), &errorResponse) - if err == nil { - return &errorResponse - } - - return nil -} - -func (e *azureErrorDetails) isEmpty() bool { - return e.Code == "" -} - -func (e *azureErrorResponse) Error() string { - var buf bytes.Buffer - //buf.WriteString("-=-=- ERROR -=-=-") - formatAzureErrorResponse(e.ErrorDetails, &buf, "") - //buf.WriteString("-=-=- ERROR -=-=-") - return buf.String() -} - -// format a Azure Error Response by recursing through the JSON structure. -// -// Errors may contain nested errors, which are JSON documents that have been -// serialized and escaped. Keep following this nesting all the way down... -func formatAzureErrorResponse(error azureErrorDetails, buf *bytes.Buffer, indent string) { - if error.isEmpty() { - return - } - - buf.WriteString(fmt.Sprintf("ERROR: %s-> %s : %s\n", indent, error.Code, error.Message)) - for _, x := range error.Details { - newIndent := fmt.Sprintf("%s ", indent) - - var aer azureErrorResponse - err := json.Unmarshal([]byte(x.Message), &aer) - if err == nil { - buf.WriteString(fmt.Sprintf("ERROR: %s-> %s\n", newIndent, x.Code)) - formatAzureErrorResponse(aer.ErrorDetails, buf, newIndent) - } else { - buf.WriteString(fmt.Sprintf("ERROR: %s-> %s : %s\n", newIndent, x.Code, x.Message)) - } - } -} diff --git a/builder/azure/dtl/azure_error_response_test.TestAzureErrorNestedShouldFormat.approved.txt b/builder/azure/dtl/azure_error_response_test.TestAzureErrorNestedShouldFormat.approved.txt deleted file mode 100644 index 39d4deeec..000000000 --- a/builder/azure/dtl/azure_error_response_test.TestAzureErrorNestedShouldFormat.approved.txt +++ /dev/null @@ -1,4 +0,0 @@ -ERROR: -> DeploymentFailed : At least one resource deployment operation failed. Please list deployment operations for details. Please see https://aka.ms/arm-debug for usage details. -ERROR: -> BadRequest -ERROR: -> InvalidRequestFormat : Cannot parse the request. -ERROR: -> InvalidJson : Error converting value "playground" to type 'Microsoft.WindowsAzure.Networking.Nrp.Frontend.Contract.Csm.Public.IpAllocationMethod'. Path 'properties.publicIPAllocationMethod', line 1, position 130. diff --git a/builder/azure/dtl/azure_error_response_test.TestAzureErrorSimpleShouldFormat.approved.txt b/builder/azure/dtl/azure_error_response_test.TestAzureErrorSimpleShouldFormat.approved.txt deleted file mode 100644 index 4b4834c62..000000000 --- a/builder/azure/dtl/azure_error_response_test.TestAzureErrorSimpleShouldFormat.approved.txt +++ /dev/null @@ -1 +0,0 @@ -ERROR: -> ResourceNotFound : The Resource 'Microsoft.Compute/images/PackerUbuntuImage' under resource group 'packer-test00' was not found. diff --git a/builder/azure/dtl/azure_error_response_test.go b/builder/azure/dtl/azure_error_response_test.go deleted file mode 100644 index fec3aa7f6..000000000 --- a/builder/azure/dtl/azure_error_response_test.go +++ /dev/null @@ -1,77 +0,0 @@ -package dtl - -import ( - "strings" - "testing" - - approvaltests "github.com/approvals/go-approval-tests" - "github.com/hashicorp/packer-plugin-sdk/json" -) - -const AzureErrorSimple = `{"error":{"code":"ResourceNotFound","message":"The Resource 'Microsoft.Compute/images/PackerUbuntuImage' under resource group 'packer-test00' was not found."}}` -const AzureErrorNested = `{"status":"Failed","error":{"code":"DeploymentFailed","message":"At least one resource deployment operation failed. Please list deployment operations for details. Please see https://aka.ms/arm-debug for usage details.","details":[{"code":"BadRequest","message":"{\r\n \"error\": {\r\n \"code\": \"InvalidRequestFormat\",\r\n \"message\": \"Cannot parse the request.\",\r\n \"details\": [\r\n {\r\n \"code\": \"InvalidJson\",\r\n \"message\": \"Error converting value \\\"playground\\\" to type 'Microsoft.WindowsAzure.Networking.Nrp.Frontend.Contract.Csm.Public.IpAllocationMethod'. Path 'properties.publicIPAllocationMethod', line 1, position 130.\"\r\n }\r\n ]\r\n }\r\n}"}]}}` - -func TestAzureErrorSimpleShouldUnmarshal(t *testing.T) { - var azureErrorResponse azureErrorResponse - err := json.Unmarshal([]byte(AzureErrorSimple), &azureErrorResponse) - if err != nil { - t.Fatal(err) - } - - if azureErrorResponse.ErrorDetails.Code != "ResourceNotFound" { - t.Errorf("Error.Code") - } - if azureErrorResponse.ErrorDetails.Message != "The Resource 'Microsoft.Compute/images/PackerUbuntuImage' under resource group 'packer-test00' was not found." { - t.Errorf("Error.Message") - } -} - -func TestAzureErrorNestedShouldUnmarshal(t *testing.T) { - var azureError azureErrorResponse - err := json.Unmarshal([]byte(AzureErrorNested), &azureError) - if err != nil { - t.Fatal(err) - } - - if azureError.ErrorDetails.Code != "DeploymentFailed" { - t.Errorf("Error.Code") - } - if !strings.HasPrefix(azureError.ErrorDetails.Message, "At least one resource deployment operation failed") { - t.Errorf("Error.Message") - } -} - -func TestAzureErrorEmptyShouldFormat(t *testing.T) { - var aer azureErrorResponse - s := aer.Error() - - if s != "" { - t.Fatalf("Expected \"\", but got %s", aer.Error()) - } -} - -func TestAzureErrorSimpleShouldFormat(t *testing.T) { - var azureErrorResponse azureErrorResponse - err := json.Unmarshal([]byte(AzureErrorSimple), &azureErrorResponse) - if err != nil { - t.Fatal(err) - } - - err = approvaltests.VerifyString(t, azureErrorResponse.Error()) - if err != nil { - t.Fatal(err) - } -} - -func TestAzureErrorNestedShouldFormat(t *testing.T) { - var azureErrorResponse azureErrorResponse - err := json.Unmarshal([]byte(AzureErrorNested), &azureErrorResponse) - if err != nil { - t.Fatal(err) - } - - err = approvaltests.VerifyString(t, azureErrorResponse.Error()) - if err != nil { - t.Fatal(err) - } -} diff --git a/builder/azure/dtl/builder.go b/builder/azure/dtl/builder.go deleted file mode 100644 index 05fd4102b..000000000 --- a/builder/azure/dtl/builder.go +++ /dev/null @@ -1,367 +0,0 @@ -package dtl - -import ( - "context" - "errors" - "fmt" - "log" - "os" - "runtime" - "strings" - - dtl "github.com/Azure/azure-sdk-for-go/services/devtestlabs/mgmt/2018-09-15/dtl" - - "github.com/Azure/go-autorest/autorest/adal" - "github.com/dgrijalva/jwt-go" - "github.com/hashicorp/hcl/v2/hcldec" - "github.com/hashicorp/packer-plugin-sdk/communicator" - "github.com/hashicorp/packer-plugin-sdk/multistep" - "github.com/hashicorp/packer-plugin-sdk/multistep/commonsteps" - packersdk "github.com/hashicorp/packer-plugin-sdk/packer" - packerAzureCommon "github.com/hashicorp/packer/builder/azure/common" - "github.com/hashicorp/packer/builder/azure/common/constants" - "github.com/hashicorp/packer/builder/azure/common/lin" -) - -type Builder struct { - config *Config - stateBag multistep.StateBag - runner multistep.Runner -} - -const ( - DefaultSasBlobContainer = "system/Microsoft.Compute" - DefaultSecretName = "packerKeyVaultSecret" -) - -func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() } - -func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) { - c, warnings, errs := newConfig(raws...) - if errs != nil { - return nil, warnings, errs - } - - b.config = c - - b.stateBag = new(multistep.BasicStateBag) - b.configureStateBag(b.stateBag) - b.setTemplateParameters(b.stateBag) - - return nil, warnings, errs -} - -func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) (packersdk.Artifact, error) { - - ui.Say("Running builder ...") - - ctx, cancel := context.WithCancel(ctx) - defer cancel() - - // FillParameters function captures authType and sets defaults. - err := b.config.ClientConfig.FillParameters() - if err != nil { - return nil, err - } - - log.Print(":: Configuration") - packerAzureCommon.DumpConfig(b.config, func(s string) { log.Print(s) }) - - b.stateBag.Put("hook", hook) - b.stateBag.Put(constants.Ui, ui) - - spnCloud, err := b.getServicePrincipalToken(ui.Say) - if err != nil { - return nil, err - } - - ui.Message("Creating Azure Resource Manager (ARM) client ...") - azureClient, err := NewAzureClient( - b.config.ClientConfig.SubscriptionID, - b.config.LabResourceGroupName, - b.config.ClientConfig.CloudEnvironment(), - b.config.SharedGalleryTimeout, - b.config.PollingDurationTimeout, - spnCloud) - - if err != nil { - return nil, err - } - - resolver := newResourceResolver(azureClient) - if err := resolver.Resolve(b.config); err != nil { - return nil, err - } - if b.config.ClientConfig.ObjectID == "" { - b.config.ClientConfig.ObjectID = getObjectIdFromToken(ui, spnCloud) - } else { - ui.Message("You have provided Object_ID which is no longer needed, azure packer builder determines this dynamically from the authentication token") - } - - if b.config.ClientConfig.ObjectID == "" && b.config.OSType != constants.Target_Linux { - return nil, fmt.Errorf("could not determine the ObjectID for the user, which is required for Windows builds") - } - - if b.config.isManagedImage() { - // If a managed image already exists it cannot be overwritten. We need to delete it if the user has provided -force flag - _, err = azureClient.DtlCustomImageClient.Get(ctx, b.config.ManagedImageResourceGroupName, b.config.LabName, b.config.ManagedImageName, "") - - if err == nil { - 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)) - f, err := azureClient.DtlCustomImageClient.Delete(ctx, b.config.ManagedImageResourceGroupName, b.config.LabName, b.config.ManagedImageName) - if err == nil { - err = f.WaitForCompletionRef(ctx, azureClient.DtlCustomImageClient.Client) - } - 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) - } - } - - } 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 https://www.packer.io/docs/builders/azure/arm#azure-arm-builder-specific-options") - } - - b.config.validateLocationZoneResiliency(ui.Say) - - b.setRuntimeParameters(b.stateBag) - b.setTemplateParameters(b.stateBag) - var steps []multistep.Step - - deploymentName := b.stateBag.Get(constants.ArmDeploymentName).(string) - - // For Managed Images, validate that Shared Gallery Image exists before publishing to SIG - if b.config.isManagedImage() && b.config.SharedGalleryDestination.SigDestinationGalleryName != "" { - _, err = azureClient.GalleryImagesClient.Get(ctx, b.config.SharedGalleryDestination.SigDestinationResourceGroup, b.config.SharedGalleryDestination.SigDestinationGalleryName, b.config.SharedGalleryDestination.SigDestinationImageName) - if err != nil { - return nil, fmt.Errorf("The Shared Gallery Image to which to publish the managed image version to does not exist in the resource group %s", b.config.SharedGalleryDestination.SigDestinationResourceGroup) - } - // SIG requires that replication regions include the region in which the Managed Image resides - managedImageLocation := normalizeAzureRegion(b.stateBag.Get(constants.ArmLocation).(string)) - foundMandatoryReplicationRegion := false - var normalizedReplicationRegions []string - for _, region := range b.config.SharedGalleryDestination.SigDestinationReplicationRegions { - // change region to lower-case and strip spaces - normalizedRegion := normalizeAzureRegion(region) - normalizedReplicationRegions = append(normalizedReplicationRegions, normalizedRegion) - if strings.EqualFold(normalizedRegion, managedImageLocation) { - foundMandatoryReplicationRegion = true - continue - } - } - if foundMandatoryReplicationRegion == false { - b.config.SharedGalleryDestination.SigDestinationReplicationRegions = append(normalizedReplicationRegions, managedImageLocation) - } - b.stateBag.Put(constants.ArmManagedImageSharedGalleryReplicationRegions, b.config.SharedGalleryDestination.SigDestinationReplicationRegions) - } - - // Find the lab location - lab, err := azureClient.DtlLabsClient.Get(ctx, b.config.LabResourceGroupName, b.config.LabName, "") - if err != nil { - return nil, fmt.Errorf("Unable to fetch the Lab %s information in %s resource group", b.config.LabName, b.config.LabResourceGroupName) - } - - b.config.Location = *lab.Location - - if b.config.LabVirtualNetworkName == "" || b.config.LabSubnetName == "" { - virtualNetowrk, subnet, err := b.getSubnetInformation(ctx, ui, *azureClient) - - if err != nil { - return nil, err - } - b.config.LabVirtualNetworkName = *virtualNetowrk - b.config.LabSubnetName = *subnet - - ui.Message(fmt.Sprintf("No lab network information provided. Using %s Virtual network and %s subnet for Virtual Machine creation", b.config.LabVirtualNetworkName, b.config.LabSubnetName)) - } - - if b.config.OSType == constants.Target_Linux { - steps = []multistep.Step{ - NewStepDeployTemplate(azureClient, ui, b.config, deploymentName, GetVirtualMachineDeployment), - &communicator.StepConnectSSH{ - Config: &b.config.Comm, - Host: lin.SSHHost, - SSHConfig: b.config.Comm.SSHConfigFunc(), - }, - &commonsteps.StepProvision{}, - &commonsteps.StepCleanupTempKeys{ - Comm: &b.config.Comm, - }, - NewStepPowerOffCompute(azureClient, ui, b.config), - NewStepCaptureImage(azureClient, ui, b.config), - NewStepPublishToSharedImageGallery(azureClient, ui, b.config), - NewStepDeleteVirtualMachine(azureClient, ui, b.config), - } - } else if b.config.OSType == constants.Target_Windows { - steps = []multistep.Step{ - NewStepDeployTemplate(azureClient, ui, b.config, deploymentName, GetVirtualMachineDeployment), - &StepSaveWinRMPassword{ - Password: b.config.tmpAdminPassword, - BuildName: b.config.PackerBuildName, - }, - &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 - }, - }, - &commonsteps.StepProvision{}, - NewStepPowerOffCompute(azureClient, ui, b.config), - NewStepCaptureImage(azureClient, ui, b.config), - NewStepPublishToSharedImageGallery(azureClient, ui, b.config), - NewStepDeleteVirtualMachine(azureClient, ui, b.config), - } - } else { - return nil, fmt.Errorf("Builder does not support the os_type '%s'", b.config.OSType) - } - - 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)) - - if len(b.config.Comm.SSHPrivateKey) != 0 { - 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) - } - } - - b.runner = commonsteps.NewRunner(steps, b.config.PackerConfig, ui) - b.runner.Run(ctx, 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.") - } - - if b.config.isManagedImage() { - managedImageID := fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Compute/images/%s", b.config.ClientConfig.SubscriptionID, b.config.ManagedImageResourceGroupName, b.config.ManagedImageName) - return NewManagedImageArtifact(b.config.OSType, b.config.ManagedImageResourceGroupName, b.config.ManagedImageName, b.config.Location, managedImageID) - } - return &Artifact{}, nil -} - -func (b *Builder) writeSSHPrivateKey(ui packersdk.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(b.config.Comm.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)) - } - } -} - -func (b *Builder) configureStateBag(stateBag multistep.StateBag) { - stateBag.Put(constants.AuthorizedKey, b.config.sshAuthorizedKey) - - stateBag.Put(constants.ArmTags, b.config.AzureTags) - stateBag.Put(constants.ArmComputeName, b.config.tmpComputeName) - stateBag.Put(constants.ArmDeploymentName, b.config.tmpDeploymentName) - stateBag.Put(constants.ArmKeyVaultName, b.config.tmpKeyVaultName) - stateBag.Put(constants.ArmNicName, b.config.tmpNicName) - stateBag.Put(constants.ArmPublicIPAddressName, b.config.tmpPublicIPAddressName) - if b.config.tmpResourceGroupName != "" { - stateBag.Put(constants.ArmResourceGroupName, b.config.tmpResourceGroupName) - stateBag.Put(constants.ArmIsExistingResourceGroup, false) - } else { - stateBag.Put(constants.ArmIsExistingResourceGroup, true) - } - - stateBag.Put(constants.ArmIsManagedImage, b.config.isManagedImage()) - stateBag.Put(constants.ArmManagedImageResourceGroupName, b.config.ManagedImageResourceGroupName) - stateBag.Put(constants.ArmManagedImageName, b.config.ManagedImageName) - if b.config.isManagedImage() && b.config.SharedGalleryDestination.SigDestinationGalleryName != "" { - stateBag.Put(constants.ArmManagedImageSigPublishResourceGroup, b.config.SharedGalleryDestination.SigDestinationResourceGroup) - stateBag.Put(constants.ArmManagedImageSharedGalleryName, b.config.SharedGalleryDestination.SigDestinationGalleryName) - stateBag.Put(constants.ArmManagedImageSharedGalleryImageName, b.config.SharedGalleryDestination.SigDestinationImageName) - stateBag.Put(constants.ArmManagedImageSharedGalleryImageVersion, b.config.SharedGalleryDestination.SigDestinationImageVersion) - stateBag.Put(constants.ArmManagedImageSubscription, b.config.ClientConfig.SubscriptionID) - } -} - -// Parameters that are only known at runtime after querying Azure. -func (b *Builder) setRuntimeParameters(stateBag multistep.StateBag) { - stateBag.Put(constants.ArmLocation, b.config.Location) -} - -func (b *Builder) setTemplateParameters(stateBag multistep.StateBag) { - stateBag.Put(constants.ArmVirtualMachineCaptureParameters, b.config.toVirtualMachineCaptureParameters()) -} - -func (b *Builder) getServicePrincipalToken(say func(string)) (*adal.ServicePrincipalToken, error) { - return b.config.ClientConfig.GetServicePrincipalToken(say, b.config.ClientConfig.CloudEnvironment().ResourceManagerEndpoint) -} - -func (b *Builder) getSubnetInformation(ctx context.Context, ui packersdk.Ui, azClient AzureClient) (*string, *string, error) { - num := int32(10) - virtualNetworkPage, err := azClient.DtlVirtualNetworksClient.List(ctx, b.config.LabResourceGroupName, b.config.LabName, "", "", &num, "") - - if err != nil { - return nil, nil, fmt.Errorf("Error retrieving Virtual networks in Resourcegroup %s", b.config.LabResourceGroupName) - } - - virtualNetworks := virtualNetworkPage.Values() - for _, virtualNetwork := range virtualNetworks { - for _, subnetOverride := range *virtualNetwork.SubnetOverrides { - // Check if the Subnet is allowed to create VMs having Public IP - if subnetOverride.UseInVMCreationPermission == dtl.Allow && subnetOverride.UsePublicIPAddressPermission == dtl.Allow { - // Return Virtual Network Name and Subnet Name - // Since we cannot query the Usage information from DTL network we cannot know the current remaining capacity. - // TODO (vaangadi) : Fix this to query the subnets that actually have space to create VM. - return virtualNetwork.Name, subnetOverride.LabSubnetName, nil - } - } - } - return nil, nil, fmt.Errorf("No available Subnet with available space in resource group %s", b.config.LabResourceGroupName) -} - -func getObjectIdFromToken(ui packersdk.Ui, token *adal.ServicePrincipalToken) string { - claims := jwt.MapClaims{} - var p jwt.Parser - - var err error - - _, _, err = p.ParseUnverified(token.OAuthToken(), claims) - - if err != nil { - ui.Error(fmt.Sprintf("Failed to parse the token,Error: %s", err.Error())) - return "" - } - return claims["oid"].(string) - -} - -func normalizeAzureRegion(name string) string { - return strings.ToLower(strings.Replace(name, " ", "", -1)) -} diff --git a/builder/azure/dtl/builder_acc_test.go b/builder/azure/dtl/builder_acc_test.go deleted file mode 100644 index bbea241f4..000000000 --- a/builder/azure/dtl/builder_acc_test.go +++ /dev/null @@ -1,142 +0,0 @@ -package dtl - -// these tests require the following variables to be set, -// although some test will only use a subset: -// -// * ARM_CLIENT_ID -// * ARM_CLIENT_SECRET -// * ARM_SUBSCRIPTION_ID -// * ARM_OBJECT_ID -// -// The subscription in question should have a resource group -// called "packer-acceptance-test" in "South Central US" region. -// This also requires a Devtest lab to be created with "packer-acceptance-test" -// name in "South Central US region. This can be achieved using the following -// az cli commands " -// az group create --name packer-acceptance-test --location "South Central US" -// az deployment group create \ -// --name ExampleDeployment \ -// --resource-group packer-acceptance-test \ -// --template-file acceptancetest.json \ - -// In addition, the PACKER_ACC variable should also be set to -// a non-empty value to enable Packer acceptance tests and the -// options "-v -timeout 90m" should be provided to the test -// command, e.g.: -// go test -v -timeout 90m -run TestBuilderAcc_.* - -import ( - "testing" - - builderT "github.com/hashicorp/packer/acctest" -) - -const DeviceLoginAcceptanceTest = "DEVICELOGIN_TEST" - -func TestBuilderAcc_ManagedDisk_Windows(t *testing.T) { - builderT.Test(t, builderT.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Builder: &Builder{}, - Template: testBuilderAccManagedDiskWindows, - }) -} -func TestBuilderAcc_ManagedDisk_Linux_Artifacts(t *testing.T) { - builderT.Test(t, builderT.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Builder: &Builder{}, - Template: testBuilderAccManagedDiskLinux, - }) -} - -func testAccPreCheck(*testing.T) {} - -const testBuilderAccManagedDiskWindows = ` -{ - "variables": { - "client_id": "{{env ` + "`ARM_CLIENT_ID`" + `}}", - "client_secret": "{{env ` + "`ARM_CLIENT_SECRET`" + `}}", - "subscription_id": "{{env ` + "`ARM_SUBSCRIPTION_ID`" + `}}", - "tenant_id": "{{env ` + "`ARM_TENANT_ID`" + `}}" - }, - "builders": [{ - "type": "test", - - "client_id": "{{user ` + "`client_id`" + `}}", - "client_secret": "{{user ` + "`client_secret`" + `}}", - "subscription_id": "{{user ` + "`subscription_id`" + `}}", - "tenant_id": "{{user ` + "`tenant_id`" + `}}", - - "lab_name": "packer-acceptance-test", - "lab_resource_group_name": "packer-acceptance-test", - "lab_virtual_network_name": "dtlpacker-acceptance-test", - - "managed_image_resource_group_name": "packer-acceptance-test", - "managed_image_name": "testBuilderAccManagedDiskWindows-{{timestamp}}", - - "os_type": "Windows", - "image_publisher": "MicrosoftWindowsServer", - "image_offer": "WindowsServer", - "image_sku": "2012-R2-Datacenter", - - "communicator": "winrm", - "winrm_use_ssl": "true", - "winrm_insecure": "true", - "winrm_timeout": "3m", - "winrm_username": "packer", - - "location": "South Central US", - "vm_size": "Standard_DS2_v2" - }] -} -` - -const testBuilderAccManagedDiskLinux = ` -{ - "variables": { - "client_id": "{{env ` + "`ARM_CLIENT_ID`" + `}}", - "client_secret": "{{env ` + "`ARM_CLIENT_SECRET`" + `}}", - "subscription_id": "{{env ` + "`ARM_SUBSCRIPTION_ID`" + `}}", - "tenant_id": "{{env ` + "`ARM_TENANT_ID`" + `}}" - }, - "builders": [{ - "type": "test", - - "client_id": "{{user ` + "`client_id`" + `}}", - "client_secret": "{{user ` + "`client_secret`" + `}}", - "subscription_id": "{{user ` + "`subscription_id`" + `}}", - - "lab_name": "packer-acceptance-test", - "lab_resource_group_name": "packer-acceptance-test", - "lab_virtual_network_name": "dtlpacker-acceptance-test", - - "managed_image_resource_group_name": "packer-acceptance-test", - "managed_image_name": "testBuilderAccManagedDiskLinux-{{timestamp}}", - - "os_type": "Linux", - "image_publisher": "Canonical", - "image_offer": "UbuntuServer", - "image_sku": "16.04-LTS", - - "location": "South Central US", - "vm_size": "Standard_DS2_v2", - - - "dtl_artifacts": [{ - "artifact_name": "linux-apt-package", - "parameters" : [{ - "name": "packages", - "value": "vim" - }, - { - "name":"update", - "value": "true" - }, - { - "name": "options", - "value": "--fix-broken" - }] - }] - - }] -} -` diff --git a/builder/azure/dtl/builder_test.go b/builder/azure/dtl/builder_test.go deleted file mode 100644 index 5120ebd40..000000000 --- a/builder/azure/dtl/builder_test.go +++ /dev/null @@ -1,33 +0,0 @@ -package dtl - -import ( - "testing" - - "github.com/hashicorp/packer/builder/azure/common/constants" -) - -func TestStateBagShouldBePopulatedExpectedValues(t *testing.T) { - var testSubject = &Builder{} - _, _, err := testSubject.Prepare(getDtlBuilderConfiguration(), getPackerConfiguration()) - if err != nil { - t.Fatalf("failed to prepare: %s", err) - } - - var expectedStateBagKeys = []string{ - constants.AuthorizedKey, - - constants.ArmTags, - constants.ArmComputeName, - constants.ArmDeploymentName, - constants.ArmNicName, - constants.ArmResourceGroupName, - constants.ArmVirtualMachineCaptureParameters, - constants.ArmPublicIPAddressName, - } - - for _, v := range expectedStateBagKeys { - if _, ok := testSubject.stateBag.GetOk(v); ok == false { - t.Errorf("Expected the builder's state bag to contain '%s', but it did not.", v) - } - } -} diff --git a/builder/azure/dtl/capture_template.go b/builder/azure/dtl/capture_template.go deleted file mode 100644 index 4f9cb7716..000000000 --- a/builder/azure/dtl/capture_template.go +++ /dev/null @@ -1,84 +0,0 @@ -package dtl - -type CaptureTemplateParameter struct { - Type string `json:"type"` - DefaultValue string `json:"defaultValue,omitempty"` -} - -type CaptureHardwareProfile struct { - VMSize string `json:"vmSize"` -} - -type CaptureUri struct { - Uri string `json:"uri"` -} - -type CaptureDisk struct { - OSType string `json:"osType"` - Name string `json:"name"` - Image CaptureUri `json:"image"` - Vhd CaptureUri `json:"vhd"` - CreateOption string `json:"createOption"` - Caching string `json:"caching"` -} - -type CaptureStorageProfile struct { - OSDisk CaptureDisk `json:"osDisk"` - DataDisks []CaptureDisk `json:"dataDisks"` -} - -type CaptureOSProfile struct { - ComputerName string `json:"computerName"` - AdminUsername string `json:"adminUsername"` - AdminPassword string `json:"adminPassword"` -} - -type CaptureNetworkInterface struct { - Id string `json:"id"` -} - -type CaptureNetworkProfile struct { - NetworkInterfaces []CaptureNetworkInterface `json:"networkInterfaces"` -} - -type CaptureBootDiagnostics struct { - Enabled bool `json:"enabled"` -} - -type CaptureDiagnosticProfile struct { - BootDiagnostics CaptureBootDiagnostics `json:"bootDiagnostics"` -} - -type CaptureProperties struct { - HardwareProfile CaptureHardwareProfile `json:"hardwareProfile"` - StorageProfile CaptureStorageProfile `json:"storageProfile"` - OSProfile CaptureOSProfile `json:"osProfile"` - NetworkProfile CaptureNetworkProfile `json:"networkProfile"` - DiagnosticsProfile CaptureDiagnosticProfile `json:"diagnosticsProfile"` - ProvisioningState int `json:"provisioningState"` -} - -type CaptureResources struct { - ApiVersion string `json:"apiVersion"` - Name string `json:"name"` - Type string `json:"type"` - Location string `json:"location"` - Properties CaptureProperties `json:"properties"` -} - -type CaptureTemplate struct { - Schema string `json:"$schema"` - ContentVersion string `json:"contentVersion"` - Parameters map[string]CaptureTemplateParameter `json:"parameters"` - Resources []CaptureResources `json:"resources"` -} - -type CaptureOperationProperties struct { - Output *CaptureTemplate `json:"output"` -} - -type CaptureOperation struct { - OperationId string `json:"operationId"` - Status string `json:"status"` - Properties *CaptureOperationProperties `json:"properties"` -} diff --git a/builder/azure/dtl/capture_template_test.go b/builder/azure/dtl/capture_template_test.go deleted file mode 100644 index 20e0d5ac9..000000000 --- a/builder/azure/dtl/capture_template_test.go +++ /dev/null @@ -1,272 +0,0 @@ -package dtl - -import ( - "encoding/json" - "testing" -) - -var captureTemplate01 = `{ - "operationId": "ac1c7c38-a591-41b3-89bd-ea39fceace1b", - "status": "Succeeded", - "startTime": "2016-04-04T21:07:25.2900874+00:00", - "endTime": "2016-04-04T21:07:26.4776321+00:00", - "properties": { - "output": { - "$schema": "http://schema.management.azure.com/schemas/2014-04-01-preview/VM_IP.json", - "contentVersion": "1.0.0.0", - "parameters": { - "vmName": { - "type": "string" - }, - "vmSize": { - "type": "string", - "defaultValue": "Standard_A2" - }, - "adminUserName": { - "type": "string" - }, - "adminPassword": { - "type": "securestring" - }, - "networkInterfaceId": { - "type": "string" - } - }, - "resources": [ - { - "apiVersion": "2015-06-15", - "properties": { - "hardwareProfile": { - "vmSize": "[parameters('vmSize')]" - }, - "storageProfile": { - "osDisk": { - "osType": "Linux", - "name": "packer-osDisk.32118633-6dc9-449f-83b6-a7d2983bec14.vhd", - "createOption": "FromImage", - "image": { - "uri": "http://storage.blob.core.windows.net/system/Microsoft.Compute/Images/images/packer-osDisk.32118633-6dc9-449f-83b6-a7d2983bec14.vhd" - }, - "vhd": { - "uri": "http://storage.blob.core.windows.net/vmcontainerce1a1b75-f480-47cb-8e6e-55142e4a5f68/osDisk.ce1a1b75-f480-47cb-8e6e-55142e4a5f68.vhd" - }, - "caching": "ReadWrite" - }, - "dataDisks": [ - { - "lun": 0, - "name": "packer-datadisk-0.32118633-6dc9-449f-83b6-a7d2983bec14.vhd", - "createOption": "Empty", - "image": { - "uri": "http://storage.blob.core.windows.net/system/Microsoft.Compute/Images/images/packer-datadisk-0.32118633-6dc9-449f-83b6-a7d2983bec14.vhd" - }, - "vhd": { - "uri": "http://storage.blob.core.windows.net/vmcontainerce1a1b75-f480-47cb-8e6e-55142e4a5f68/datadisk-0.ce1a1b75-f480-47cb-8e6e-55142e4a5f68.vhd" - }, - "caching": "ReadWrite" - }, - { - "lun": 1, - "name": "packer-datadisk-1.32118633-6dc9-449f-83b6-a7d2983bec14.vhd", - "createOption": "Empty", - "image": { - "uri": "http://storage.blob.core.windows.net/system/Microsoft.Compute/Images/images/packer-datadisk-1.32118633-6dc9-449f-83b6-a7d2983bec14.vhd" - }, - "vhd": { - "uri": "http://storage.blob.core.windows.net/vmcontainerce1a1b75-f480-47cb-8e6e-55142e4a5f68/datadisk-1.ce1a1b75-f480-47cb-8e6e-55142e4a5f68.vhd" - }, - "caching": "ReadWrite" - } - ] - }, - "osProfile": { - "computerName": "[parameters('vmName')]", - "adminUsername": "[parameters('adminUsername')]", - "adminPassword": "[parameters('adminPassword')]" - }, - "networkProfile": { - "networkInterfaces": [ - { - "id": "[parameters('networkInterfaceId')]" - } - ] - }, - "diagnosticsProfile": { - "bootDiagnostics": { - "enabled": false - } - }, - "provisioningState": 0 - }, - "name": "[parameters('vmName')]", - "type": "Microsoft.Compute/virtualMachines", - "location": "southcentralus" - } - ] - } - } -}` - -var captureTemplate02 = `{ - "operationId": "ac1c7c38-a591-41b3-89bd-ea39fceace1b", - "status": "Succeeded", - "startTime": "2016-04-04T21:07:25.2900874+00:00", - "endTime": "2016-04-04T21:07:26.4776321+00:00" -}` - -func TestCaptureParseJson(t *testing.T) { - var operation CaptureOperation - err := json.Unmarshal([]byte(captureTemplate01), &operation) - if err != nil { - t.Fatalf("failed to the sample capture operation: %s", err) - } - - testSubject := operation.Properties.Output - if testSubject.Schema != "http://schema.management.azure.com/schemas/2014-04-01-preview/VM_IP.json" { - t.Errorf("Schema's value was unexpected: %s", testSubject.Schema) - } - if testSubject.ContentVersion != "1.0.0.0" { - t.Errorf("ContentVersion's value was unexpected: %s", testSubject.ContentVersion) - } - - // == Parameters ==================================== - if len(testSubject.Parameters) != 5 { - t.Fatalf("expected parameters to have 5 keys, but got %d", len(testSubject.Parameters)) - } - if _, ok := testSubject.Parameters["vmName"]; !ok { - t.Errorf("Parameters['vmName'] was an expected parameters, but it did not exist") - } - if testSubject.Parameters["vmName"].Type != "string" { - t.Errorf("Parameters['vmName'].Type == 'string', but got '%s'", testSubject.Parameters["vmName"].Type) - } - if _, ok := testSubject.Parameters["vmSize"]; !ok { - t.Errorf("Parameters['vmSize'] was an expected parameters, but it did not exist") - } - if testSubject.Parameters["vmSize"].Type != "string" { - t.Errorf("Parameters['vmSize'].Type == 'string', but got '%s'", testSubject.Parameters["vmSize"]) - } - if testSubject.Parameters["vmSize"].DefaultValue != "Standard_A2" { - t.Errorf("Parameters['vmSize'].DefaultValue == 'string', but got '%s'", testSubject.Parameters["vmSize"].DefaultValue) - } - - // == Resources ===================================== - if len(testSubject.Resources) != 1 { - t.Fatalf("expected resources to have length 1, but got %d", len(testSubject.Resources)) - } - if testSubject.Resources[0].Name != "[parameters('vmName')]" { - t.Errorf("Resources[0].Name's value was unexpected: %s", testSubject.Resources[0].Name) - } - if testSubject.Resources[0].Type != "Microsoft.Compute/virtualMachines" { - t.Errorf("Resources[0].Type's value was unexpected: %s", testSubject.Resources[0].Type) - } - if testSubject.Resources[0].Location != "southcentralus" { - t.Errorf("Resources[0].Location's value was unexpected: %s", testSubject.Resources[0].Location) - } - - // == Resources/Properties ===================================== - if testSubject.Resources[0].Properties.ProvisioningState != 0 { - t.Errorf("Resources[0].Properties.ProvisioningState's value was unexpected: %d", testSubject.Resources[0].Properties.ProvisioningState) - } - - // == Resources/Properties/HardwareProfile ====================== - hardwareProfile := testSubject.Resources[0].Properties.HardwareProfile - if hardwareProfile.VMSize != "[parameters('vmSize')]" { - t.Errorf("Resources[0].Properties.HardwareProfile.VMSize's value was unexpected: %s", hardwareProfile.VMSize) - } - - // == Resources/Properties/StorageProfile/OSDisk ================ - osDisk := testSubject.Resources[0].Properties.StorageProfile.OSDisk - if osDisk.OSType != "Linux" { - t.Errorf("Resources[0].Properties.StorageProfile.OSDisk.OSDisk's value was unexpected: %s", osDisk.OSType) - } - if osDisk.Name != "packer-osDisk.32118633-6dc9-449f-83b6-a7d2983bec14.vhd" { - t.Errorf("Resources[0].Properties.StorageProfile.OSDisk.Name's value was unexpected: %s", osDisk.Name) - } - if osDisk.CreateOption != "FromImage" { - t.Errorf("Resources[0].Properties.StorageProfile.OSDisk.CreateOption's value was unexpected: %s", osDisk.CreateOption) - } - if osDisk.Image.Uri != "http://storage.blob.core.windows.net/system/Microsoft.Compute/Images/images/packer-osDisk.32118633-6dc9-449f-83b6-a7d2983bec14.vhd" { - t.Errorf("Resources[0].Properties.StorageProfile.OSDisk.Image.Uri's value was unexpected: %s", osDisk.Image.Uri) - } - if osDisk.Vhd.Uri != "http://storage.blob.core.windows.net/vmcontainerce1a1b75-f480-47cb-8e6e-55142e4a5f68/osDisk.ce1a1b75-f480-47cb-8e6e-55142e4a5f68.vhd" { - t.Errorf("Resources[0].Properties.StorageProfile.OSDisk.Vhd.Uri's value was unexpected: %s", osDisk.Vhd.Uri) - } - if osDisk.Caching != "ReadWrite" { - t.Errorf("Resources[0].Properties.StorageProfile.OSDisk.Caching's value was unexpected: %s", osDisk.Caching) - } - - // == Resources/Properties/StorageProfile/DataDisks ================ - dataDisks := testSubject.Resources[0].Properties.StorageProfile.DataDisks - if len(dataDisks) != 2 { - t.Errorf("Resources[0].Properties.StorageProfile.DataDisks, 2 disks expected but was: %d", len(dataDisks)) - } - if dataDisks[0].Name != "packer-datadisk-0.32118633-6dc9-449f-83b6-a7d2983bec14.vhd" { - t.Errorf("Resources[0].Properties.StorageProfile.dataDisks[0].Name's value was unexpected: %s", dataDisks[0].Name) - } - if dataDisks[0].CreateOption != "Empty" { - t.Errorf("Resources[0].Properties.StorageProfile.dataDisks[0].CreateOption's value was unexpected: %s", dataDisks[0].CreateOption) - } - if dataDisks[0].Image.Uri != "http://storage.blob.core.windows.net/system/Microsoft.Compute/Images/images/packer-datadisk-0.32118633-6dc9-449f-83b6-a7d2983bec14.vhd" { - t.Errorf("Resources[0].Properties.StorageProfile.dataDisks[0].Image.Uri's value was unexpected: %s", dataDisks[0].Image.Uri) - } - if dataDisks[0].Vhd.Uri != "http://storage.blob.core.windows.net/vmcontainerce1a1b75-f480-47cb-8e6e-55142e4a5f68/datadisk-0.ce1a1b75-f480-47cb-8e6e-55142e4a5f68.vhd" { - t.Errorf("Resources[0].Properties.StorageProfile.dataDisks[0].Vhd.Uri's value was unexpected: %s", dataDisks[0].Vhd.Uri) - } - if dataDisks[0].Caching != "ReadWrite" { - t.Errorf("Resources[0].Properties.StorageProfile.dataDisks[0].Caching's value was unexpected: %s", dataDisks[0].Caching) - } - if dataDisks[1].Name != "packer-datadisk-1.32118633-6dc9-449f-83b6-a7d2983bec14.vhd" { - t.Errorf("Resources[0].Properties.StorageProfile.dataDisks[1].Name's value was unexpected: %s", dataDisks[1].Name) - } - if dataDisks[1].CreateOption != "Empty" { - t.Errorf("Resources[0].Properties.StorageProfile.dataDisks[1].CreateOption's value was unexpected: %s", dataDisks[1].CreateOption) - } - if dataDisks[1].Image.Uri != "http://storage.blob.core.windows.net/system/Microsoft.Compute/Images/images/packer-datadisk-1.32118633-6dc9-449f-83b6-a7d2983bec14.vhd" { - t.Errorf("Resources[0].Properties.StorageProfile.dataDisks[1].Image.Uri's value was unexpected: %s", dataDisks[1].Image.Uri) - } - if dataDisks[1].Vhd.Uri != "http://storage.blob.core.windows.net/vmcontainerce1a1b75-f480-47cb-8e6e-55142e4a5f68/datadisk-1.ce1a1b75-f480-47cb-8e6e-55142e4a5f68.vhd" { - t.Errorf("Resources[0].Properties.StorageProfile.dataDisks[1].Vhd.Uri's value was unexpected: %s", dataDisks[1].Vhd.Uri) - } - if dataDisks[1].Caching != "ReadWrite" { - t.Errorf("Resources[0].Properties.StorageProfile.dataDisks[1].Caching's value was unexpected: %s", dataDisks[1].Caching) - } - - // == Resources/Properties/OSProfile ============================ - osProfile := testSubject.Resources[0].Properties.OSProfile - if osProfile.AdminPassword != "[parameters('adminPassword')]" { - t.Errorf("Resources[0].Properties.OSProfile.AdminPassword's value was unexpected: %s", osProfile.AdminPassword) - } - if osProfile.AdminUsername != "[parameters('adminUsername')]" { - t.Errorf("Resources[0].Properties.OSProfile.AdminUsername's value was unexpected: %s", osProfile.AdminUsername) - } - if osProfile.ComputerName != "[parameters('vmName')]" { - t.Errorf("Resources[0].Properties.OSProfile.ComputerName's value was unexpected: %s", osProfile.ComputerName) - } - - // == Resources/Properties/NetworkProfile ======================= - networkProfile := testSubject.Resources[0].Properties.NetworkProfile - if len(networkProfile.NetworkInterfaces) != 1 { - t.Errorf("Count of Resources[0].Properties.NetworkProfile.NetworkInterfaces was expected to be 1, but go %d", len(networkProfile.NetworkInterfaces)) - } - if networkProfile.NetworkInterfaces[0].Id != "[parameters('networkInterfaceId')]" { - t.Errorf("Resources[0].Properties.NetworkProfile.NetworkInterfaces[0].Id's value was unexpected: %s", networkProfile.NetworkInterfaces[0].Id) - } - - // == Resources/Properties/DiagnosticsProfile =================== - diagnosticsProfile := testSubject.Resources[0].Properties.DiagnosticsProfile - if diagnosticsProfile.BootDiagnostics.Enabled != false { - t.Errorf("Resources[0].Properties.DiagnosticsProfile.BootDiagnostics.Enabled's value was unexpected: %t", diagnosticsProfile.BootDiagnostics.Enabled) - } -} - -func TestCaptureEmptyOperationJson(t *testing.T) { - var operation CaptureOperation - err := json.Unmarshal([]byte(captureTemplate02), &operation) - if err != nil { - t.Fatalf("failed to the sample capture operation: %s", err) - } - - if operation.Properties != nil { - t.Errorf("JSON contained no properties, but value was not nil: %+v", operation.Properties) - } -} diff --git a/builder/azure/dtl/config.go b/builder/azure/dtl/config.go deleted file mode 100644 index da9214b1e..000000000 --- a/builder/azure/dtl/config.go +++ /dev/null @@ -1,767 +0,0 @@ -//go:generate packer-sdc struct-markdown -//go:generate packer-sdc mapstructure-to-hcl2 -type Config,SharedImageGallery,SharedImageGalleryDestination,DtlArtifact,ArtifactParameter - -package dtl - -import ( - "crypto/rand" - "crypto/rsa" - "crypto/x509" - "crypto/x509/pkix" - "encoding/base64" - "encoding/json" - "fmt" - "math/big" - "regexp" - "strings" - "time" - - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2018-04-01/compute" - "github.com/Azure/go-autorest/autorest/to" - "github.com/masterzen/winrm" - - "github.com/hashicorp/packer/builder/azure/common/client" - "github.com/hashicorp/packer/builder/azure/common/constants" - - "github.com/hashicorp/packer-plugin-sdk/common" - "github.com/hashicorp/packer-plugin-sdk/communicator" - packersdk "github.com/hashicorp/packer-plugin-sdk/packer" - "github.com/hashicorp/packer-plugin-sdk/template/config" - "github.com/hashicorp/packer-plugin-sdk/template/interpolate" - "github.com/hashicorp/packer/builder/azure/pkcs12" - - "golang.org/x/crypto/ssh" -) - -const ( - DefaultImageVersion = "latest" - DefaultUserName = "packer" - DefaultPrivateVirtualNetworkWithPublicIp = false - DefaultVMSize = "Standard_A1" -) - -const ( - // https://docs.microsoft.com/en-us/azure/architecture/best-practices/naming-conventions#naming-rules-and-restrictions - // Regular expressions in Go are not expressive enough, such that the regular expression returned by Azure - // can be used (no backtracking). - // - // -> ^[^_\W][\w-._]{0,79}(? 0 { - return nil, nil, errs - } - - return &c, nil, nil -} - -func setSshValues(c *Config) error { - if c.Comm.SSHTimeout == 0 { - c.Comm.SSHTimeout = 20 * time.Minute - } - - if c.Comm.SSHPrivateKeyFile != "" { - privateKeyBytes, err := c.Comm.ReadSSHPrivateKeyFile() - if err != nil { - return err - } - signer, err := ssh.ParsePrivateKey(privateKeyBytes) - if err != nil { - return err - } - - publicKey := signer.PublicKey() - c.sshAuthorizedKey = fmt.Sprintf("%s %s packer Azure Deployment%s", - publicKey.Type(), - base64.StdEncoding.EncodeToString(publicKey.Marshal()), - time.Now().Format(time.RFC3339)) - c.Comm.SSHPrivateKey = privateKeyBytes - - } else { - sshKeyPair, err := NewOpenSshKeyPair() - if err != nil { - return err - } - - c.sshAuthorizedKey = sshKeyPair.AuthorizedKey() - c.Comm.SSHPrivateKey = sshKeyPair.PrivateKey() - } - - return nil -} - -func setWinRMCertificate(c *Config) error { - c.Comm.WinRMTransportDecorator = - func() winrm.Transporter { - return &winrm.ClientNTLM{} - } - - cert, password, err := c.createCertificate() - - c.winrmCertificate = cert - c.winrmPassword = password - - return err -} - -func setRuntimeValues(c *Config) { - var tempName = NewTempName(c) - - c.tmpAdminPassword = tempName.AdminPassword - packersdk.LogSecretFilter.Set(c.tmpAdminPassword) - - c.tmpCertificatePassword = tempName.CertificatePassword - c.tmpComputeName = tempName.ComputeName - - c.tmpDeploymentName = tempName.DeploymentName - if c.LabResourceGroupName == "" { - c.tmpResourceGroupName = tempName.ResourceGroupName - } else { - c.tmpResourceGroupName = c.LabResourceGroupName - } - c.tmpNicName = tempName.NicName - c.tmpPublicIPAddressName = tempName.PublicIPAddressName - c.tmpOSDiskName = tempName.OSDiskName - c.tmpSubnetName = tempName.SubnetName - c.tmpVirtualNetworkName = tempName.VirtualNetworkName - c.tmpKeyVaultName = tempName.KeyVaultName -} - -func setUserNamePassword(c *Config) { - if c.Comm.SSHUsername == "" { - c.Comm.SSHUsername = DefaultUserName - } - - c.UserName = c.Comm.SSHUsername - - if c.Comm.SSHPassword != "" { - c.Password = c.Comm.SSHPassword - } else { - c.Password = c.tmpAdminPassword - } -} - -func provideDefaultValues(c *Config) { - if c.VMSize == "" { - c.VMSize = DefaultVMSize - } - - if c.ManagedImageStorageAccountType == "" { - c.managedImageStorageAccountType = compute.StorageAccountTypesStandardLRS - } - - if c.DiskCachingType == "" { - c.diskCachingType = compute.CachingTypesReadWrite - } - - if c.ImagePublisher != "" && c.ImageVersion == "" { - c.ImageVersion = DefaultImageVersion - } -} - -func assertTagProperties(c *Config, errs *packersdk.MultiError) { - if len(c.AzureTags) > 15 { - errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("a max of 15 tags are supported, but %d were provided", len(c.AzureTags))) - } - - for k, v := range c.AzureTags { - if len(k) > 512 { - errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("the tag name %q exceeds (%d) the 512 character limit", k, len(k))) - } - if len(*v) > 256 { - errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("the tag name %q exceeds (%d) the 256 character limit", *v, len(*v))) - } - } -} - -func assertRequiredParametersSet(c *Config, errs *packersdk.MultiError) { - c.ClientConfig.Validate(errs) - - ///////////////////////////////////////////// - // Capture - if c.CaptureContainerName == "" && c.ManagedImageName == "" { - errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("A capture_container_name or managed_image_name must be specified")) - } - - if c.CaptureNamePrefix == "" && c.ManagedImageResourceGroupName == "" { - errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("A capture_name_prefix or managed_image_resource_group_name must be specified")) - } - - if (c.CaptureNamePrefix != "" || c.CaptureContainerName != "") && (c.ManagedImageResourceGroupName != "" || c.ManagedImageName != "") { - errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("Either a VHD or a managed image can be built, but not both. Please specify either capture_container_name and capture_name_prefix or managed_image_resource_group_name and managed_image_name.")) - } - - if c.CaptureContainerName != "" { - if !reCaptureContainerName.MatchString(c.CaptureContainerName) { - errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("A capture_container_name must satisfy the regular expression %q.", reCaptureContainerName.String())) - } - - if strings.HasSuffix(c.CaptureContainerName, "-") { - errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("A capture_container_name must not end with a hyphen, e.g. '-'.")) - } - - if strings.Contains(c.CaptureContainerName, "--") { - errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("A capture_container_name must not contain consecutive hyphens, e.g. '--'.")) - } - - if c.CaptureNamePrefix == "" { - errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("A capture_name_prefix must be specified")) - } - - if !reCaptureNamePrefix.MatchString(c.CaptureNamePrefix) { - errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("A capture_name_prefix must satisfy the regular expression %q.", reCaptureNamePrefix.String())) - } - - if strings.HasSuffix(c.CaptureNamePrefix, "-") || strings.HasSuffix(c.CaptureNamePrefix, ".") { - errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("A capture_name_prefix must not end with a hyphen or period.")) - } - } - - if c.LabResourceGroupName == "" { - errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("The settings lab_resource_group_name needs to be defined.")) - } - - ///////////////////////////////////////////// - // Compute - toInt := func(b bool) int { - if b { - return 1 - } else { - return 0 - } - } - - isImageUrl := c.ImageUrl != "" - isCustomManagedImage := c.CustomManagedImageName != "" || c.CustomManagedImageResourceGroupName != "" - isSharedGallery := c.SharedGallery.GalleryName != "" - isPlatformImage := c.ImagePublisher != "" || c.ImageOffer != "" || c.ImageSku != "" - - countSourceInputs := toInt(isImageUrl) + toInt(isCustomManagedImage) + toInt(isPlatformImage) + toInt(isSharedGallery) - - if countSourceInputs > 1 { - errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("Specify either a VHD (image_url), Image Reference (image_publisher, image_offer, image_sku), a Managed Disk (custom_managed_disk_image_name, custom_managed_disk_resource_group_name), or a Shared Gallery Image (shared_image_gallery)")) - } - - if isImageUrl && c.ManagedImageResourceGroupName != "" { - errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("A managed image must be created from a managed image, it cannot be created from a VHD.")) - } - - if c.SharedGallery.GalleryName != "" { - if c.SharedGallery.Subscription == "" { - errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("A shared_image_gallery.subscription must be specified")) - } - if c.SharedGallery.ResourceGroup == "" { - errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("A shared_image_gallery.resource_group must be specified")) - } - if c.SharedGallery.ImageName == "" { - errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("A shared_image_gallery.image_name must be specified")) - } - if c.CaptureContainerName != "" { - errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("VHD Target [capture_container_name] is not supported when using Shared Image Gallery as source. Use managed_image_resource_group_name instead.")) - } - if c.CaptureNamePrefix != "" { - errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("VHD Target [capture_name_prefix] is not supported when using Shared Image Gallery as source. Use managed_image_name instead.")) - } - } else if c.ImageUrl == "" && c.CustomManagedImageName == "" { - if c.ImagePublisher == "" { - errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("An image_publisher must be specified")) - } - if c.ImageOffer == "" { - errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("An image_offer must be specified")) - } - if c.ImageSku == "" { - errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("An image_sku must be specified")) - } - } else if c.ImageUrl == "" && c.ImagePublisher == "" { - if c.CustomManagedImageResourceGroupName == "" { - errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("An custom_managed_image_resource_group_name must be specified")) - } - if c.CustomManagedImageName == "" { - errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("A custom_managed_image_name must be specified")) - } - if c.ManagedImageResourceGroupName == "" { - errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("An managed_image_resource_group_name must be specified")) - } - if c.ManagedImageName == "" { - errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("An managed_image_name must be specified")) - } - } else { - if c.ImagePublisher != "" || c.ImageOffer != "" || c.ImageSku != "" || c.ImageVersion != "" { - errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("An image_url must not be specified if image_publisher, image_offer, image_sku, or image_version is specified")) - } - } - - if c.ManagedImageResourceGroupName != "" { - if ok, err := assertResourceGroupName(c.ManagedImageResourceGroupName, "managed_image_resource_group_name"); !ok { - errs = packersdk.MultiErrorAppend(errs, err) - } - } - - if c.ManagedImageName != "" { - if ok, err := assertManagedImageName(c.ManagedImageName, "managed_image_name"); !ok { - errs = packersdk.MultiErrorAppend(errs, err) - } - } - - if c.LabVirtualNetworkName == "" && c.LabResourceGroupName != "" { - errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("If lab_resource_group_name is specified, so must lab_virtual_network_name")) - } - if c.LabVirtualNetworkName == "" && c.LabSubnetName != "" { - errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("If virtual_network_subnet_name is specified, so must lab_virtual_network_name")) - } - - ///////////////////////////////////////////// - // Polling Duration Timeout - if c.PollingDurationTimeout == 0 { - // In the sdk, the default is 15 m. - c.PollingDurationTimeout = 15 * time.Minute - } - - ///////////////////////////////////////////// - // OS - if strings.EqualFold(c.OSType, constants.Target_Linux) { - c.OSType = constants.Target_Linux - } else if strings.EqualFold(c.OSType, constants.Target_Windows) { - c.OSType = constants.Target_Windows - } else if c.OSType == "" { - errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("An os_type must be specified")) - } else { - errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("The os_type %q is invalid", c.OSType)) - } - - switch c.ManagedImageStorageAccountType { - case "", string(compute.StorageAccountTypesStandardLRS): - c.managedImageStorageAccountType = compute.StorageAccountTypesStandardLRS - case string(compute.StorageAccountTypesPremiumLRS): - c.managedImageStorageAccountType = compute.StorageAccountTypesPremiumLRS - default: - errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("The managed_image_storage_account_type %q is invalid", c.ManagedImageStorageAccountType)) - } - // Errs check to make the linter happy. - if errs != nil { - return - } -} - -func assertManagedImageName(name, setting string) (bool, error) { - if !isValidAzureName(reManagedDiskName, name) { - return false, fmt.Errorf("The setting %s must match the regular expression %q, and not end with a '-' or '.'.", setting, validManagedDiskName) - } - return true, nil -} - -func assertResourceGroupName(rgn, setting string) (bool, error) { - if !isValidAzureName(reResourceGroupName, rgn) { - return false, fmt.Errorf("The setting %s must match the regular expression %q, and not end with a '-' or '.'.", setting, validResourceGroupNameRe) - } - return true, nil -} - -func isValidAzureName(re *regexp.Regexp, rgn string) bool { - return re.Match([]byte(rgn)) && - !strings.HasSuffix(rgn, ".") && - !strings.HasSuffix(rgn, "-") -} - -func (c *Config) validateLocationZoneResiliency(say func(s string)) { - // Docs on regions that support Availibility Zones: - // https://docs.microsoft.com/en-us/azure/availability-zones/az-overview#regions-that-support-availability-zones - // Query technical names for locations: - // az account list-locations --query '[].name' -o tsv - - var zones = make(map[string]struct{}) - zones["westeurope"] = struct{}{} - zones["centralus"] = struct{}{} - zones["eastus2"] = struct{}{} - zones["francecentral"] = struct{}{} - zones["northeurope"] = struct{}{} - zones["southeastasia"] = struct{}{} - zones["westus2"] = struct{}{} - - if _, ok := zones[c.Location]; !ok { - say(fmt.Sprintf("WARNING: Zone resiliency may not be supported in %s, checkout the docs at https://docs.microsoft.com/en-us/azure/availability-zones/", c.Location)) - } -} diff --git a/builder/azure/dtl/config.hcl2spec.go b/builder/azure/dtl/config.hcl2spec.go deleted file mode 100644 index d0dd2c2d0..000000000 --- a/builder/azure/dtl/config.hcl2spec.go +++ /dev/null @@ -1,349 +0,0 @@ -// Code generated by "packer-sdc mapstructure-to-hcl2"; DO NOT EDIT. - -package dtl - -import ( - "github.com/hashicorp/hcl/v2/hcldec" - "github.com/zclconf/go-cty/cty" -) - -// FlatArtifactParameter is an auto-generated flat version of ArtifactParameter. -// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. -type FlatArtifactParameter struct { - Name *string `mapstructure:"name" cty:"name" hcl:"name"` - Value *string `mapstructure:"value" cty:"value" hcl:"value"` - Type *string `mapstructure:"type" cty:"type" hcl:"type"` -} - -// FlatMapstructure returns a new FlatArtifactParameter. -// FlatArtifactParameter is an auto-generated flat version of ArtifactParameter. -// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*ArtifactParameter) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { - return new(FlatArtifactParameter) -} - -// HCL2Spec returns the hcl spec of a ArtifactParameter. -// This spec is used by HCL to read the fields of ArtifactParameter. -// The decoded values from this spec will then be applied to a FlatArtifactParameter. -func (*FlatArtifactParameter) HCL2Spec() map[string]hcldec.Spec { - s := map[string]hcldec.Spec{ - "name": &hcldec.AttrSpec{Name: "name", Type: cty.String, Required: false}, - "value": &hcldec.AttrSpec{Name: "value", Type: cty.String, Required: false}, - "type": &hcldec.AttrSpec{Name: "type", Type: cty.String, Required: false}, - } - return s -} - -// FlatConfig is an auto-generated flat version of Config. -// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. -type FlatConfig struct { - PackerBuildName *string `mapstructure:"packer_build_name" cty:"packer_build_name" hcl:"packer_build_name"` - PackerBuilderType *string `mapstructure:"packer_builder_type" cty:"packer_builder_type" hcl:"packer_builder_type"` - PackerCoreVersion *string `mapstructure:"packer_core_version" cty:"packer_core_version" hcl:"packer_core_version"` - PackerDebug *bool `mapstructure:"packer_debug" cty:"packer_debug" hcl:"packer_debug"` - PackerForce *bool `mapstructure:"packer_force" cty:"packer_force" hcl:"packer_force"` - PackerOnError *string `mapstructure:"packer_on_error" cty:"packer_on_error" hcl:"packer_on_error"` - PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables" hcl:"packer_user_variables"` - PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables" hcl:"packer_sensitive_variables"` - CloudEnvironmentName *string `mapstructure:"cloud_environment_name" required:"false" cty:"cloud_environment_name" hcl:"cloud_environment_name"` - ClientID *string `mapstructure:"client_id" cty:"client_id" hcl:"client_id"` - ClientSecret *string `mapstructure:"client_secret" cty:"client_secret" hcl:"client_secret"` - ClientCertPath *string `mapstructure:"client_cert_path" cty:"client_cert_path" hcl:"client_cert_path"` - ClientCertExpireTimeout *string `mapstructure:"client_cert_token_timeout" required:"false" cty:"client_cert_token_timeout" hcl:"client_cert_token_timeout"` - ClientJWT *string `mapstructure:"client_jwt" cty:"client_jwt" hcl:"client_jwt"` - ObjectID *string `mapstructure:"object_id" cty:"object_id" hcl:"object_id"` - TenantID *string `mapstructure:"tenant_id" required:"false" cty:"tenant_id" hcl:"tenant_id"` - SubscriptionID *string `mapstructure:"subscription_id" cty:"subscription_id" hcl:"subscription_id"` - UseAzureCLIAuth *bool `mapstructure:"use_azure_cli_auth" required:"false" cty:"use_azure_cli_auth" hcl:"use_azure_cli_auth"` - CaptureNamePrefix *string `mapstructure:"capture_name_prefix" cty:"capture_name_prefix" hcl:"capture_name_prefix"` - CaptureContainerName *string `mapstructure:"capture_container_name" cty:"capture_container_name" hcl:"capture_container_name"` - SharedGallery *FlatSharedImageGallery `mapstructure:"shared_image_gallery" cty:"shared_image_gallery" hcl:"shared_image_gallery"` - SharedGalleryDestination *FlatSharedImageGalleryDestination `mapstructure:"shared_image_gallery_destination" cty:"shared_image_gallery_destination" hcl:"shared_image_gallery_destination"` - SharedGalleryTimeout *string `mapstructure:"shared_image_gallery_timeout" cty:"shared_image_gallery_timeout" hcl:"shared_image_gallery_timeout"` - ImagePublisher *string `mapstructure:"image_publisher" cty:"image_publisher" hcl:"image_publisher"` - ImageOffer *string `mapstructure:"image_offer" cty:"image_offer" hcl:"image_offer"` - ImageSku *string `mapstructure:"image_sku" cty:"image_sku" hcl:"image_sku"` - ImageVersion *string `mapstructure:"image_version" cty:"image_version" hcl:"image_version"` - ImageUrl *string `mapstructure:"image_url" cty:"image_url" hcl:"image_url"` - CustomManagedImageResourceGroupName *string `mapstructure:"custom_managed_image_resource_group_name" cty:"custom_managed_image_resource_group_name" hcl:"custom_managed_image_resource_group_name"` - CustomManagedImageName *string `mapstructure:"custom_managed_image_name" cty:"custom_managed_image_name" hcl:"custom_managed_image_name"` - Location *string `mapstructure:"location" cty:"location" hcl:"location"` - VMSize *string `mapstructure:"vm_size" cty:"vm_size" hcl:"vm_size"` - ManagedImageResourceGroupName *string `mapstructure:"managed_image_resource_group_name" cty:"managed_image_resource_group_name" hcl:"managed_image_resource_group_name"` - ManagedImageName *string `mapstructure:"managed_image_name" cty:"managed_image_name" hcl:"managed_image_name"` - ManagedImageStorageAccountType *string `mapstructure:"managed_image_storage_account_type" required:"false" cty:"managed_image_storage_account_type" hcl:"managed_image_storage_account_type"` - AzureTags map[string]*string `mapstructure:"azure_tags" required:"false" cty:"azure_tags" hcl:"azure_tags"` - PlanID *string `mapstructure:"plan_id" required:"false" cty:"plan_id" hcl:"plan_id"` - PollingDurationTimeout *string `mapstructure:"polling_duration_timeout" required:"false" cty:"polling_duration_timeout" hcl:"polling_duration_timeout"` - OSType *string `mapstructure:"os_type" required:"false" cty:"os_type" hcl:"os_type"` - OSDiskSizeGB *int32 `mapstructure:"os_disk_size_gb" required:"false" cty:"os_disk_size_gb" hcl:"os_disk_size_gb"` - AdditionalDiskSize []int32 `mapstructure:"disk_additional_size" required:"false" cty:"disk_additional_size" hcl:"disk_additional_size"` - DiskCachingType *string `mapstructure:"disk_caching_type" required:"false" cty:"disk_caching_type" hcl:"disk_caching_type"` - StorageType *string `mapstructure:"storage_type" cty:"storage_type" hcl:"storage_type"` - LabVirtualNetworkName *string `mapstructure:"lab_virtual_network_name" cty:"lab_virtual_network_name" hcl:"lab_virtual_network_name"` - LabName *string `mapstructure:"lab_name" cty:"lab_name" hcl:"lab_name"` - LabSubnetName *string `mapstructure:"lab_subnet_name" cty:"lab_subnet_name" hcl:"lab_subnet_name"` - LabResourceGroupName *string `mapstructure:"lab_resource_group_name" cty:"lab_resource_group_name" hcl:"lab_resource_group_name"` - DtlArtifacts []FlatDtlArtifact `mapstructure:"dtl_artifacts" cty:"dtl_artifacts" hcl:"dtl_artifacts"` - VMName *string `mapstructure:"vm_name" cty:"vm_name" hcl:"vm_name"` - UserName *string `cty:"user_name" hcl:"user_name"` - Password *string `cty:"password" hcl:"password"` - VMCreationResourceGroup *string `cty:"vm_creation_resource_group" hcl:"vm_creation_resource_group"` - Type *string `mapstructure:"communicator" cty:"communicator" hcl:"communicator"` - PauseBeforeConnect *string `mapstructure:"pause_before_connecting" cty:"pause_before_connecting" hcl:"pause_before_connecting"` - SSHHost *string `mapstructure:"ssh_host" cty:"ssh_host" hcl:"ssh_host"` - SSHPort *int `mapstructure:"ssh_port" cty:"ssh_port" hcl:"ssh_port"` - SSHUsername *string `mapstructure:"ssh_username" cty:"ssh_username" hcl:"ssh_username"` - SSHPassword *string `mapstructure:"ssh_password" cty:"ssh_password" hcl:"ssh_password"` - SSHKeyPairName *string `mapstructure:"ssh_keypair_name" undocumented:"true" cty:"ssh_keypair_name" hcl:"ssh_keypair_name"` - SSHTemporaryKeyPairName *string `mapstructure:"temporary_key_pair_name" undocumented:"true" cty:"temporary_key_pair_name" hcl:"temporary_key_pair_name"` - SSHTemporaryKeyPairType *string `mapstructure:"temporary_key_pair_type" cty:"temporary_key_pair_type" hcl:"temporary_key_pair_type"` - SSHTemporaryKeyPairBits *int `mapstructure:"temporary_key_pair_bits" cty:"temporary_key_pair_bits" hcl:"temporary_key_pair_bits"` - SSHCiphers []string `mapstructure:"ssh_ciphers" cty:"ssh_ciphers" hcl:"ssh_ciphers"` - SSHClearAuthorizedKeys *bool `mapstructure:"ssh_clear_authorized_keys" cty:"ssh_clear_authorized_keys" hcl:"ssh_clear_authorized_keys"` - SSHKEXAlgos []string `mapstructure:"ssh_key_exchange_algorithms" cty:"ssh_key_exchange_algorithms" hcl:"ssh_key_exchange_algorithms"` - SSHPrivateKeyFile *string `mapstructure:"ssh_private_key_file" undocumented:"true" cty:"ssh_private_key_file" hcl:"ssh_private_key_file"` - SSHCertificateFile *string `mapstructure:"ssh_certificate_file" cty:"ssh_certificate_file" hcl:"ssh_certificate_file"` - SSHPty *bool `mapstructure:"ssh_pty" cty:"ssh_pty" hcl:"ssh_pty"` - SSHTimeout *string `mapstructure:"ssh_timeout" cty:"ssh_timeout" hcl:"ssh_timeout"` - SSHWaitTimeout *string `mapstructure:"ssh_wait_timeout" undocumented:"true" cty:"ssh_wait_timeout" hcl:"ssh_wait_timeout"` - SSHAgentAuth *bool `mapstructure:"ssh_agent_auth" undocumented:"true" cty:"ssh_agent_auth" hcl:"ssh_agent_auth"` - SSHDisableAgentForwarding *bool `mapstructure:"ssh_disable_agent_forwarding" cty:"ssh_disable_agent_forwarding" hcl:"ssh_disable_agent_forwarding"` - SSHHandshakeAttempts *int `mapstructure:"ssh_handshake_attempts" cty:"ssh_handshake_attempts" hcl:"ssh_handshake_attempts"` - SSHBastionHost *string `mapstructure:"ssh_bastion_host" cty:"ssh_bastion_host" hcl:"ssh_bastion_host"` - SSHBastionPort *int `mapstructure:"ssh_bastion_port" cty:"ssh_bastion_port" hcl:"ssh_bastion_port"` - SSHBastionAgentAuth *bool `mapstructure:"ssh_bastion_agent_auth" cty:"ssh_bastion_agent_auth" hcl:"ssh_bastion_agent_auth"` - SSHBastionUsername *string `mapstructure:"ssh_bastion_username" cty:"ssh_bastion_username" hcl:"ssh_bastion_username"` - SSHBastionPassword *string `mapstructure:"ssh_bastion_password" cty:"ssh_bastion_password" hcl:"ssh_bastion_password"` - SSHBastionInteractive *bool `mapstructure:"ssh_bastion_interactive" cty:"ssh_bastion_interactive" hcl:"ssh_bastion_interactive"` - SSHBastionPrivateKeyFile *string `mapstructure:"ssh_bastion_private_key_file" cty:"ssh_bastion_private_key_file" hcl:"ssh_bastion_private_key_file"` - SSHBastionCertificateFile *string `mapstructure:"ssh_bastion_certificate_file" cty:"ssh_bastion_certificate_file" hcl:"ssh_bastion_certificate_file"` - SSHFileTransferMethod *string `mapstructure:"ssh_file_transfer_method" cty:"ssh_file_transfer_method" hcl:"ssh_file_transfer_method"` - SSHProxyHost *string `mapstructure:"ssh_proxy_host" cty:"ssh_proxy_host" hcl:"ssh_proxy_host"` - SSHProxyPort *int `mapstructure:"ssh_proxy_port" cty:"ssh_proxy_port" hcl:"ssh_proxy_port"` - SSHProxyUsername *string `mapstructure:"ssh_proxy_username" cty:"ssh_proxy_username" hcl:"ssh_proxy_username"` - SSHProxyPassword *string `mapstructure:"ssh_proxy_password" cty:"ssh_proxy_password" hcl:"ssh_proxy_password"` - SSHKeepAliveInterval *string `mapstructure:"ssh_keep_alive_interval" cty:"ssh_keep_alive_interval" hcl:"ssh_keep_alive_interval"` - SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout" hcl:"ssh_read_write_timeout"` - SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels" hcl:"ssh_remote_tunnels"` - SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels" hcl:"ssh_local_tunnels"` - SSHPublicKey []byte `mapstructure:"ssh_public_key" undocumented:"true" cty:"ssh_public_key" hcl:"ssh_public_key"` - SSHPrivateKey []byte `mapstructure:"ssh_private_key" undocumented:"true" cty:"ssh_private_key" hcl:"ssh_private_key"` - WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username" hcl:"winrm_username"` - WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password" hcl:"winrm_password"` - WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host" hcl:"winrm_host"` - WinRMNoProxy *bool `mapstructure:"winrm_no_proxy" cty:"winrm_no_proxy" hcl:"winrm_no_proxy"` - WinRMPort *int `mapstructure:"winrm_port" cty:"winrm_port" hcl:"winrm_port"` - WinRMTimeout *string `mapstructure:"winrm_timeout" cty:"winrm_timeout" hcl:"winrm_timeout"` - WinRMUseSSL *bool `mapstructure:"winrm_use_ssl" cty:"winrm_use_ssl" hcl:"winrm_use_ssl"` - WinRMInsecure *bool `mapstructure:"winrm_insecure" cty:"winrm_insecure" hcl:"winrm_insecure"` - WinRMUseNTLM *bool `mapstructure:"winrm_use_ntlm" cty:"winrm_use_ntlm" hcl:"winrm_use_ntlm"` -} - -// FlatMapstructure returns a new FlatConfig. -// FlatConfig is an auto-generated flat version of Config. -// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { - return new(FlatConfig) -} - -// HCL2Spec returns the hcl spec of a Config. -// This spec is used by HCL to read the fields of Config. -// The decoded values from this spec will then be applied to a FlatConfig. -func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { - s := map[string]hcldec.Spec{ - "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, - "packer_builder_type": &hcldec.AttrSpec{Name: "packer_builder_type", Type: cty.String, Required: false}, - "packer_core_version": &hcldec.AttrSpec{Name: "packer_core_version", Type: cty.String, Required: false}, - "packer_debug": &hcldec.AttrSpec{Name: "packer_debug", Type: cty.Bool, Required: false}, - "packer_force": &hcldec.AttrSpec{Name: "packer_force", Type: cty.Bool, Required: false}, - "packer_on_error": &hcldec.AttrSpec{Name: "packer_on_error", Type: cty.String, Required: false}, - "packer_user_variables": &hcldec.AttrSpec{Name: "packer_user_variables", Type: cty.Map(cty.String), Required: false}, - "packer_sensitive_variables": &hcldec.AttrSpec{Name: "packer_sensitive_variables", Type: cty.List(cty.String), Required: false}, - "cloud_environment_name": &hcldec.AttrSpec{Name: "cloud_environment_name", Type: cty.String, Required: false}, - "client_id": &hcldec.AttrSpec{Name: "client_id", Type: cty.String, Required: false}, - "client_secret": &hcldec.AttrSpec{Name: "client_secret", Type: cty.String, Required: false}, - "client_cert_path": &hcldec.AttrSpec{Name: "client_cert_path", Type: cty.String, Required: false}, - "client_cert_token_timeout": &hcldec.AttrSpec{Name: "client_cert_token_timeout", Type: cty.String, Required: false}, - "client_jwt": &hcldec.AttrSpec{Name: "client_jwt", Type: cty.String, Required: false}, - "object_id": &hcldec.AttrSpec{Name: "object_id", Type: cty.String, Required: false}, - "tenant_id": &hcldec.AttrSpec{Name: "tenant_id", Type: cty.String, Required: false}, - "subscription_id": &hcldec.AttrSpec{Name: "subscription_id", Type: cty.String, Required: false}, - "use_azure_cli_auth": &hcldec.AttrSpec{Name: "use_azure_cli_auth", Type: cty.Bool, Required: false}, - "capture_name_prefix": &hcldec.AttrSpec{Name: "capture_name_prefix", Type: cty.String, Required: false}, - "capture_container_name": &hcldec.AttrSpec{Name: "capture_container_name", Type: cty.String, Required: false}, - "shared_image_gallery": &hcldec.BlockSpec{TypeName: "shared_image_gallery", Nested: hcldec.ObjectSpec((*FlatSharedImageGallery)(nil).HCL2Spec())}, - "shared_image_gallery_destination": &hcldec.BlockSpec{TypeName: "shared_image_gallery_destination", Nested: hcldec.ObjectSpec((*FlatSharedImageGalleryDestination)(nil).HCL2Spec())}, - "shared_image_gallery_timeout": &hcldec.AttrSpec{Name: "shared_image_gallery_timeout", Type: cty.String, Required: false}, - "image_publisher": &hcldec.AttrSpec{Name: "image_publisher", Type: cty.String, Required: false}, - "image_offer": &hcldec.AttrSpec{Name: "image_offer", Type: cty.String, Required: false}, - "image_sku": &hcldec.AttrSpec{Name: "image_sku", Type: cty.String, Required: false}, - "image_version": &hcldec.AttrSpec{Name: "image_version", Type: cty.String, Required: false}, - "image_url": &hcldec.AttrSpec{Name: "image_url", Type: cty.String, Required: false}, - "custom_managed_image_resource_group_name": &hcldec.AttrSpec{Name: "custom_managed_image_resource_group_name", Type: cty.String, Required: false}, - "custom_managed_image_name": &hcldec.AttrSpec{Name: "custom_managed_image_name", Type: cty.String, Required: false}, - "location": &hcldec.AttrSpec{Name: "location", Type: cty.String, Required: false}, - "vm_size": &hcldec.AttrSpec{Name: "vm_size", Type: cty.String, Required: false}, - "managed_image_resource_group_name": &hcldec.AttrSpec{Name: "managed_image_resource_group_name", Type: cty.String, Required: false}, - "managed_image_name": &hcldec.AttrSpec{Name: "managed_image_name", Type: cty.String, Required: false}, - "managed_image_storage_account_type": &hcldec.AttrSpec{Name: "managed_image_storage_account_type", Type: cty.String, Required: false}, - "azure_tags": &hcldec.AttrSpec{Name: "azure_tags", Type: cty.Map(cty.String), Required: false}, - "plan_id": &hcldec.AttrSpec{Name: "plan_id", Type: cty.String, Required: false}, - "polling_duration_timeout": &hcldec.AttrSpec{Name: "polling_duration_timeout", Type: cty.String, Required: false}, - "os_type": &hcldec.AttrSpec{Name: "os_type", Type: cty.String, Required: false}, - "os_disk_size_gb": &hcldec.AttrSpec{Name: "os_disk_size_gb", Type: cty.Number, Required: false}, - "disk_additional_size": &hcldec.AttrSpec{Name: "disk_additional_size", Type: cty.List(cty.Number), Required: false}, - "disk_caching_type": &hcldec.AttrSpec{Name: "disk_caching_type", Type: cty.String, Required: false}, - "storage_type": &hcldec.AttrSpec{Name: "storage_type", Type: cty.String, Required: false}, - "lab_virtual_network_name": &hcldec.AttrSpec{Name: "lab_virtual_network_name", Type: cty.String, Required: false}, - "lab_name": &hcldec.AttrSpec{Name: "lab_name", Type: cty.String, Required: false}, - "lab_subnet_name": &hcldec.AttrSpec{Name: "lab_subnet_name", Type: cty.String, Required: false}, - "lab_resource_group_name": &hcldec.AttrSpec{Name: "lab_resource_group_name", Type: cty.String, Required: false}, - "dtl_artifacts": &hcldec.BlockListSpec{TypeName: "dtl_artifacts", Nested: hcldec.ObjectSpec((*FlatDtlArtifact)(nil).HCL2Spec())}, - "vm_name": &hcldec.AttrSpec{Name: "vm_name", Type: cty.String, Required: false}, - "user_name": &hcldec.AttrSpec{Name: "user_name", Type: cty.String, Required: false}, - "password": &hcldec.AttrSpec{Name: "password", Type: cty.String, Required: false}, - "vm_creation_resource_group": &hcldec.AttrSpec{Name: "vm_creation_resource_group", Type: cty.String, Required: false}, - "communicator": &hcldec.AttrSpec{Name: "communicator", Type: cty.String, Required: false}, - "pause_before_connecting": &hcldec.AttrSpec{Name: "pause_before_connecting", Type: cty.String, Required: false}, - "ssh_host": &hcldec.AttrSpec{Name: "ssh_host", Type: cty.String, Required: false}, - "ssh_port": &hcldec.AttrSpec{Name: "ssh_port", Type: cty.Number, Required: false}, - "ssh_username": &hcldec.AttrSpec{Name: "ssh_username", Type: cty.String, Required: false}, - "ssh_password": &hcldec.AttrSpec{Name: "ssh_password", Type: cty.String, Required: false}, - "ssh_keypair_name": &hcldec.AttrSpec{Name: "ssh_keypair_name", Type: cty.String, Required: false}, - "temporary_key_pair_name": &hcldec.AttrSpec{Name: "temporary_key_pair_name", Type: cty.String, Required: false}, - "temporary_key_pair_type": &hcldec.AttrSpec{Name: "temporary_key_pair_type", Type: cty.String, Required: false}, - "temporary_key_pair_bits": &hcldec.AttrSpec{Name: "temporary_key_pair_bits", Type: cty.Number, Required: false}, - "ssh_ciphers": &hcldec.AttrSpec{Name: "ssh_ciphers", Type: cty.List(cty.String), Required: false}, - "ssh_clear_authorized_keys": &hcldec.AttrSpec{Name: "ssh_clear_authorized_keys", Type: cty.Bool, Required: false}, - "ssh_key_exchange_algorithms": &hcldec.AttrSpec{Name: "ssh_key_exchange_algorithms", Type: cty.List(cty.String), Required: false}, - "ssh_private_key_file": &hcldec.AttrSpec{Name: "ssh_private_key_file", Type: cty.String, Required: false}, - "ssh_certificate_file": &hcldec.AttrSpec{Name: "ssh_certificate_file", Type: cty.String, Required: false}, - "ssh_pty": &hcldec.AttrSpec{Name: "ssh_pty", Type: cty.Bool, Required: false}, - "ssh_timeout": &hcldec.AttrSpec{Name: "ssh_timeout", Type: cty.String, Required: false}, - "ssh_wait_timeout": &hcldec.AttrSpec{Name: "ssh_wait_timeout", Type: cty.String, Required: false}, - "ssh_agent_auth": &hcldec.AttrSpec{Name: "ssh_agent_auth", Type: cty.Bool, Required: false}, - "ssh_disable_agent_forwarding": &hcldec.AttrSpec{Name: "ssh_disable_agent_forwarding", Type: cty.Bool, Required: false}, - "ssh_handshake_attempts": &hcldec.AttrSpec{Name: "ssh_handshake_attempts", Type: cty.Number, Required: false}, - "ssh_bastion_host": &hcldec.AttrSpec{Name: "ssh_bastion_host", Type: cty.String, Required: false}, - "ssh_bastion_port": &hcldec.AttrSpec{Name: "ssh_bastion_port", Type: cty.Number, Required: false}, - "ssh_bastion_agent_auth": &hcldec.AttrSpec{Name: "ssh_bastion_agent_auth", Type: cty.Bool, Required: false}, - "ssh_bastion_username": &hcldec.AttrSpec{Name: "ssh_bastion_username", Type: cty.String, Required: false}, - "ssh_bastion_password": &hcldec.AttrSpec{Name: "ssh_bastion_password", Type: cty.String, Required: false}, - "ssh_bastion_interactive": &hcldec.AttrSpec{Name: "ssh_bastion_interactive", Type: cty.Bool, Required: false}, - "ssh_bastion_private_key_file": &hcldec.AttrSpec{Name: "ssh_bastion_private_key_file", Type: cty.String, Required: false}, - "ssh_bastion_certificate_file": &hcldec.AttrSpec{Name: "ssh_bastion_certificate_file", Type: cty.String, Required: false}, - "ssh_file_transfer_method": &hcldec.AttrSpec{Name: "ssh_file_transfer_method", Type: cty.String, Required: false}, - "ssh_proxy_host": &hcldec.AttrSpec{Name: "ssh_proxy_host", Type: cty.String, Required: false}, - "ssh_proxy_port": &hcldec.AttrSpec{Name: "ssh_proxy_port", Type: cty.Number, Required: false}, - "ssh_proxy_username": &hcldec.AttrSpec{Name: "ssh_proxy_username", Type: cty.String, Required: false}, - "ssh_proxy_password": &hcldec.AttrSpec{Name: "ssh_proxy_password", Type: cty.String, Required: false}, - "ssh_keep_alive_interval": &hcldec.AttrSpec{Name: "ssh_keep_alive_interval", Type: cty.String, Required: false}, - "ssh_read_write_timeout": &hcldec.AttrSpec{Name: "ssh_read_write_timeout", Type: cty.String, Required: false}, - "ssh_remote_tunnels": &hcldec.AttrSpec{Name: "ssh_remote_tunnels", Type: cty.List(cty.String), Required: false}, - "ssh_local_tunnels": &hcldec.AttrSpec{Name: "ssh_local_tunnels", Type: cty.List(cty.String), Required: false}, - "ssh_public_key": &hcldec.AttrSpec{Name: "ssh_public_key", Type: cty.List(cty.Number), Required: false}, - "ssh_private_key": &hcldec.AttrSpec{Name: "ssh_private_key", Type: cty.List(cty.Number), Required: false}, - "winrm_username": &hcldec.AttrSpec{Name: "winrm_username", Type: cty.String, Required: false}, - "winrm_password": &hcldec.AttrSpec{Name: "winrm_password", Type: cty.String, Required: false}, - "winrm_host": &hcldec.AttrSpec{Name: "winrm_host", Type: cty.String, Required: false}, - "winrm_no_proxy": &hcldec.AttrSpec{Name: "winrm_no_proxy", Type: cty.Bool, Required: false}, - "winrm_port": &hcldec.AttrSpec{Name: "winrm_port", Type: cty.Number, Required: false}, - "winrm_timeout": &hcldec.AttrSpec{Name: "winrm_timeout", Type: cty.String, Required: false}, - "winrm_use_ssl": &hcldec.AttrSpec{Name: "winrm_use_ssl", Type: cty.Bool, Required: false}, - "winrm_insecure": &hcldec.AttrSpec{Name: "winrm_insecure", Type: cty.Bool, Required: false}, - "winrm_use_ntlm": &hcldec.AttrSpec{Name: "winrm_use_ntlm", Type: cty.Bool, Required: false}, - } - return s -} - -// FlatDtlArtifact is an auto-generated flat version of DtlArtifact. -// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. -type FlatDtlArtifact struct { - ArtifactName *string `mapstructure:"artifact_name" cty:"artifact_name" hcl:"artifact_name"` - RepositoryName *string `mapstructure:"repository_name" cty:"repository_name" hcl:"repository_name"` - ArtifactId *string `mapstructure:"artifact_id" cty:"artifact_id" hcl:"artifact_id"` - Parameters []FlatArtifactParameter `mapstructure:"parameters" cty:"parameters" hcl:"parameters"` -} - -// FlatMapstructure returns a new FlatDtlArtifact. -// FlatDtlArtifact is an auto-generated flat version of DtlArtifact. -// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*DtlArtifact) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { - return new(FlatDtlArtifact) -} - -// HCL2Spec returns the hcl spec of a DtlArtifact. -// This spec is used by HCL to read the fields of DtlArtifact. -// The decoded values from this spec will then be applied to a FlatDtlArtifact. -func (*FlatDtlArtifact) HCL2Spec() map[string]hcldec.Spec { - s := map[string]hcldec.Spec{ - "artifact_name": &hcldec.AttrSpec{Name: "artifact_name", Type: cty.String, Required: false}, - "repository_name": &hcldec.AttrSpec{Name: "repository_name", Type: cty.String, Required: false}, - "artifact_id": &hcldec.AttrSpec{Name: "artifact_id", Type: cty.String, Required: false}, - "parameters": &hcldec.BlockListSpec{TypeName: "parameters", Nested: hcldec.ObjectSpec((*FlatArtifactParameter)(nil).HCL2Spec())}, - } - return s -} - -// FlatSharedImageGallery is an auto-generated flat version of SharedImageGallery. -// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. -type FlatSharedImageGallery struct { - Subscription *string `mapstructure:"subscription" cty:"subscription" hcl:"subscription"` - ResourceGroup *string `mapstructure:"resource_group" cty:"resource_group" hcl:"resource_group"` - GalleryName *string `mapstructure:"gallery_name" cty:"gallery_name" hcl:"gallery_name"` - ImageName *string `mapstructure:"image_name" cty:"image_name" hcl:"image_name"` - ImageVersion *string `mapstructure:"image_version" cty:"image_version" hcl:"image_version"` -} - -// FlatMapstructure returns a new FlatSharedImageGallery. -// FlatSharedImageGallery is an auto-generated flat version of SharedImageGallery. -// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*SharedImageGallery) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { - return new(FlatSharedImageGallery) -} - -// HCL2Spec returns the hcl spec of a SharedImageGallery. -// This spec is used by HCL to read the fields of SharedImageGallery. -// The decoded values from this spec will then be applied to a FlatSharedImageGallery. -func (*FlatSharedImageGallery) HCL2Spec() map[string]hcldec.Spec { - s := map[string]hcldec.Spec{ - "subscription": &hcldec.AttrSpec{Name: "subscription", Type: cty.String, Required: false}, - "resource_group": &hcldec.AttrSpec{Name: "resource_group", Type: cty.String, Required: false}, - "gallery_name": &hcldec.AttrSpec{Name: "gallery_name", Type: cty.String, Required: false}, - "image_name": &hcldec.AttrSpec{Name: "image_name", Type: cty.String, Required: false}, - "image_version": &hcldec.AttrSpec{Name: "image_version", Type: cty.String, Required: false}, - } - return s -} - -// FlatSharedImageGalleryDestination is an auto-generated flat version of SharedImageGalleryDestination. -// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. -type FlatSharedImageGalleryDestination struct { - SigDestinationResourceGroup *string `mapstructure:"resource_group" cty:"resource_group" hcl:"resource_group"` - SigDestinationGalleryName *string `mapstructure:"gallery_name" cty:"gallery_name" hcl:"gallery_name"` - SigDestinationImageName *string `mapstructure:"image_name" cty:"image_name" hcl:"image_name"` - SigDestinationImageVersion *string `mapstructure:"image_version" cty:"image_version" hcl:"image_version"` - SigDestinationReplicationRegions []string `mapstructure:"replication_regions" cty:"replication_regions" hcl:"replication_regions"` -} - -// FlatMapstructure returns a new FlatSharedImageGalleryDestination. -// FlatSharedImageGalleryDestination is an auto-generated flat version of SharedImageGalleryDestination. -// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*SharedImageGalleryDestination) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { - return new(FlatSharedImageGalleryDestination) -} - -// HCL2Spec returns the hcl spec of a SharedImageGalleryDestination. -// This spec is used by HCL to read the fields of SharedImageGalleryDestination. -// The decoded values from this spec will then be applied to a FlatSharedImageGalleryDestination. -func (*FlatSharedImageGalleryDestination) HCL2Spec() map[string]hcldec.Spec { - s := map[string]hcldec.Spec{ - "resource_group": &hcldec.AttrSpec{Name: "resource_group", Type: cty.String, Required: false}, - "gallery_name": &hcldec.AttrSpec{Name: "gallery_name", Type: cty.String, Required: false}, - "image_name": &hcldec.AttrSpec{Name: "image_name", Type: cty.String, Required: false}, - "image_version": &hcldec.AttrSpec{Name: "image_version", Type: cty.String, Required: false}, - "replication_regions": &hcldec.AttrSpec{Name: "replication_regions", Type: cty.List(cty.String), Required: false}, - } - return s -} diff --git a/builder/azure/dtl/config_test.go b/builder/azure/dtl/config_test.go deleted file mode 100644 index 5690ffe68..000000000 --- a/builder/azure/dtl/config_test.go +++ /dev/null @@ -1,543 +0,0 @@ -package dtl - -import ( - "fmt" - "testing" - "time" - - "github.com/hashicorp/packer/builder/azure/common/constants" -) - -// List of configuration parameters that are required by the DTL builder. -var requiredConfigValues = []string{ - "capture_name_prefix", - "capture_container_name", - "client_id", - "client_secret", - "image_offer", - "image_publisher", - "image_sku", - "location", - "os_type", - "subscription_id", - "lab_resource_group_name", - "lab_virtual_network_name", -} - -func TestConfigShouldProvideReasonableDefaultValues(t *testing.T) { - c, _, err := newConfig(getDtlBuilderConfiguration(), getPackerConfiguration()) - - if err != nil { - t.Error("Expected configuration creation to succeed, but it failed!\n") - t.Fatalf(" errors: %s\n", err) - } - - if c.UserName == "" { - t.Error("Expected 'UserName' to be populated, but it was empty!") - } - - if c.VMSize == "" { - t.Error("Expected 'VMSize' to be populated, but it was empty!") - } - - if c.ClientConfig.ObjectID != "" { - t.Errorf("Expected 'ObjectID' to be nil, but it was '%s'!", c.ClientConfig.ObjectID) - } - - if c.managedImageStorageAccountType == "" { - t.Errorf("Expected 'managedImageStorageAccountType' to be populated, but it was empty!") - } - - if c.diskCachingType == "" { - t.Errorf("Expected 'diskCachingType' to be populated, but it was empty!") - } -} - -func TestConfigShouldDefaultVMSizeToStandardA1(t *testing.T) { - c, _, _ := newConfig(getDtlBuilderConfiguration(), getPackerConfiguration()) - - if c.VMSize != "Standard_A1" { - t.Errorf("Expected 'VMSize' to default to 'Standard_A1', but got '%s'.", c.VMSize) - } -} - -func TestConfigShouldDefaultImageVersionToLatest(t *testing.T) { - c, _, _ := newConfig(getDtlBuilderConfiguration(), getPackerConfiguration()) - - if c.ImageVersion != "latest" { - t.Errorf("Expected 'ImageVersion' to default to 'latest', but got '%s'.", c.ImageVersion) - } -} - -// The user can pass the value virtual_network_resource_group_name to avoid the lookup of -// a virtual network's resource group, or to help with disambiguation. The value should -// only be set if virtual_network_name was set. -func TestConfigVirtualNetworkResourceGroupNameMustBeSetWithVirtualNetworkName(t *testing.T) { - config := map[string]string{ - "capture_name_prefix": "ignore", - "capture_container_name": "ignore", - "location": "ignore", - "image_url": "ignore", - "subscription_id": "ignore", - "os_type": constants.Target_Linux, - "communicator": "none", - "virtual_network_resource_group_name": "MyVirtualNetworkRG", - } - - _, _, err := newConfig(config, getPackerConfiguration()) - if err == nil { - t.Error("Expected Config to reject virtual_network_resource_group_name, if virtual_network_name is not set.") - } -} - -// The user can pass the value virtual_network_subnet_name to avoid the lookup of -// a virtual network subnet's name, or to help with disambiguation. The value should -// only be set if virtual_network_name was set. -func TestConfigVirtualNetworkSubnetNameMustBeSetWithVirtualNetworkName(t *testing.T) { - config := map[string]string{ - "capture_name_prefix": "ignore", - "capture_container_name": "ignore", - "location": "ignore", - "image_url": "ignore", - "subscription_id": "ignore", - "os_type": constants.Target_Linux, - "communicator": "none", - "virtual_network_subnet_name": "MyVirtualNetworkRG", - } - - _, _, err := newConfig(config, getPackerConfiguration()) - if err == nil { - t.Error("Expected Config to reject virtual_network_subnet_name, if virtual_network_name is not set.") - } -} - -func TestSystemShouldDefineRuntimeValues(t *testing.T) { - c, _, _ := newConfig(getDtlBuilderConfiguration(), getPackerConfiguration()) - - if c.Password == "" { - t.Errorf("Expected Password to not be empty, but it was '%s'!", c.Password) - } - - if c.tmpComputeName == "" { - t.Errorf("Expected tmpComputeName to not be empty, but it was '%s'!", c.tmpComputeName) - } - - if c.tmpDeploymentName == "" { - t.Errorf("Expected tmpDeploymentName to not be empty, but it was '%s'!", c.tmpDeploymentName) - } - - if c.tmpResourceGroupName == "" { - t.Errorf("Expected tmpResourceGroupName to not be empty, but it was '%s'!", c.tmpResourceGroupName) - } - - if c.tmpOSDiskName == "" { - t.Errorf("Expected tmpOSDiskName to not be empty, but it was '%s'!", c.tmpOSDiskName) - } -} - -func TestConfigShouldTransformToVirtualMachineCaptureParameters(t *testing.T) { - c, _, _ := newConfig(getDtlBuilderConfiguration(), getPackerConfiguration()) - parameters := c.toVirtualMachineCaptureParameters() - - if *parameters.DestinationContainerName != c.CaptureContainerName { - t.Errorf("Expected DestinationContainerName to be equal to config's CaptureContainerName, but they were '%s' and '%s' respectively.", *parameters.DestinationContainerName, c.CaptureContainerName) - } - - if *parameters.VhdPrefix != c.CaptureNamePrefix { - t.Errorf("Expected DestinationContainerName to be equal to config's CaptureContainerName, but they were '%s' and '%s' respectively.", *parameters.VhdPrefix, c.CaptureNamePrefix) - } - - if *parameters.OverwriteVhds != false { - t.Error("Expected OverwriteVhds to be false, but it was not.") - } -} - -func TestConfigShouldSupportPackersConfigElements(t *testing.T) { - c, _, err := newConfig( - getDtlBuilderConfiguration(), - getPackerConfiguration(), - getPackerCommunicatorConfiguration()) - - if err != nil { - t.Fatal(err) - } - - if c.Comm.SSHTimeout != 1*time.Hour { - t.Errorf("Expected Comm.SSHTimeout to be a duration of an hour, but got '%s' instead.", c.Comm.SSHTimeout) - } - - if c.Comm.WinRMTimeout != 2*time.Hour { - t.Errorf("Expected Comm.WinRMTimeout to be a durationof two hours, but got '%s' instead.", c.Comm.WinRMTimeout) - } -} - -func TestWinRMConfigShouldSetRoundTripDecorator(t *testing.T) { - config := getDtlBuilderConfiguration() - config["communicator"] = "winrm" - config["winrm_username"] = "username" - config["winrm_password"] = "password" - - c, _, err := newConfig(config, getPackerConfiguration()) - if err != nil { - t.Fatal(err) - } - - if c.Comm.WinRMTransportDecorator == nil { - t.Error("Expected WinRMTransportDecorator to be set, but it was nil") - } -} - -func TestUserDeviceLoginIsEnabledForLinux(t *testing.T) { - config := map[string]string{ - "capture_name_prefix": "ignore", - "capture_container_name": "ignore", - "image_offer": "ignore", - "image_publisher": "ignore", - "image_sku": "ignore", - "location": "ignore", - "subscription_id": "ignore", - "os_type": constants.Target_Linux, - "communicator": "none", - "lab_resource_group_name": "ignore", - "lab_virtual_network_name": "ignore", - } - - _, _, err := newConfig(config, getPackerConfiguration()) - if err != nil { - t.Fatalf("failed to use device login for Linux: %s", err) - } -} - -func TestConfigShouldAcceptTags(t *testing.T) { - config := map[string]interface{}{ - "capture_name_prefix": "ignore", - "capture_container_name": "ignore", - "image_offer": "ignore", - "image_publisher": "ignore", - "image_sku": "ignore", - "location": "ignore", - "subscription_id": "ignore", - "communicator": "none", - "lab_resource_group_name": "ignore", - "lab_virtual_network_name": "ignore", - // Does not matter for this test case, just pick one. - "os_type": constants.Target_Linux, - "azure_tags": map[string]string{ - "tag01": "value01", - "tag02": "value02", - }, - } - - c, _, err := newConfig(config, getPackerConfiguration()) - - if err != nil { - t.Fatal(err) - } - - if len(c.AzureTags) != 2 { - t.Fatalf("expected to find 2 tags, but got %d", len(c.AzureTags)) - } - - if _, ok := c.AzureTags["tag01"]; !ok { - t.Error("expected to find key=\"tag01\", but did not") - } - if _, ok := c.AzureTags["tag02"]; !ok { - t.Error("expected to find key=\"tag02\", but did not") - } - - value := c.AzureTags["tag01"] - if *value != "value01" { - t.Errorf("expected AzureTags[\"tag01\"] to have value \"value01\", but got %q", *value) - } - - value = c.AzureTags["tag02"] - if *value != "value02" { - t.Errorf("expected AzureTags[\"tag02\"] to have value \"value02\", but got %q", *value) - } -} - -func TestConfigShouldRejectTagsInExcessOf15AcceptTags(t *testing.T) { - tooManyTags := map[string]string{} - for i := 0; i < 16; i++ { - tooManyTags[fmt.Sprintf("tag%.2d", i)] = "ignored" - } - - config := map[string]interface{}{ - "capture_name_prefix": "ignore", - "capture_container_name": "ignore", - "image_offer": "ignore", - "image_publisher": "ignore", - "image_sku": "ignore", - "location": "ignore", - "subscription_id": "ignore", - "communicator": "none", - "lab_resource_group_name": "ignore", - "lab_virtual_network_name": "ignore", - // Does not matter for this test case, just pick one. - "os_type": constants.Target_Linux, - "azure_tags": tooManyTags, - } - - _, _, err := newConfig(config, getPackerConfiguration()) - - if err == nil { - t.Fatal("expected config to reject based on an excessive amount of tags (> 15)") - } -} - -func TestConfigShouldRejectExcessiveTagNameLength(t *testing.T) { - nameTooLong := make([]byte, 513) - for i := range nameTooLong { - nameTooLong[i] = 'a' - } - - tags := map[string]string{} - tags[string(nameTooLong)] = "ignored" - - config := map[string]interface{}{ - "capture_name_prefix": "ignore", - "capture_container_name": "ignore", - "image_offer": "ignore", - "image_publisher": "ignore", - "image_sku": "ignore", - "location": "ignore", - "subscription_id": "ignore", - "communicator": "none", - "lab_resource_group_name": "ignore", - "lab_virtual_network_name": "ignore", - // Does not matter for this test case, just pick one. - "os_type": constants.Target_Linux, - "azure_tags": tags, - } - - _, _, err := newConfig(config, getPackerConfiguration()) - if err == nil { - t.Fatal("expected config to reject tag name based on length (> 512)") - } -} - -func TestConfigShouldRejectExcessiveTagValueLength(t *testing.T) { - valueTooLong := make([]byte, 257) - for i := range valueTooLong { - valueTooLong[i] = 'a' - } - - tags := map[string]string{} - tags["tag01"] = string(valueTooLong) - - config := map[string]interface{}{ - "capture_name_prefix": "ignore", - "capture_container_name": "ignore", - "image_offer": "ignore", - "image_publisher": "ignore", - "image_sku": "ignore", - "location": "ignore", - "subscription_id": "ignore", - "communicator": "none", - "lab_resource_group_name": "ignore", - "lab_virtual_network_name": "ignore", - // Does not matter for this test case, just pick one. - "os_type": constants.Target_Linux, - "azure_tags": tags, - } - - _, _, err := newConfig(config, getPackerConfiguration()) - if err == nil { - t.Fatal("expected config to reject tag value based on length (> 256)") - } -} - -func TestConfigShouldAcceptPlatformManagedImageBuild(t *testing.T) { - config := map[string]interface{}{ - "image_offer": "ignore", - "image_publisher": "ignore", - "image_sku": "ignore", - "location": "ignore", - "subscription_id": "ignore", - "communicator": "none", - "managed_image_resource_group_name": "ignore", - "managed_image_name": "ignore", - "lab_resource_group_name": "ignore", - "lab_virtual_network_name": "ignore", - - // Does not matter for this test case, just pick one. - "os_type": constants.Target_Linux, - } - - _, _, err := newConfig(config, getPackerConfiguration()) - if err != nil { - t.Fatal("expected config to accept platform managed image build") - } -} - -func TestConfigShouldAcceptManagedImageStorageAccountTypes(t *testing.T) { - config := map[string]interface{}{ - "custom_managed_image_resource_group_name": "ignore", - "custom_managed_image_name": "ignore", - "location": "ignore", - "subscription_id": "ignore", - "communicator": "none", - "managed_image_resource_group_name": "ignore", - "managed_image_name": "ignore", - "lab_resource_group_name": "ignore", - "lab_virtual_network_name": "ignore", - - // Does not matter for this test case, just pick one. - "os_type": constants.Target_Linux, - } - - storage_account_types := []string{"Premium_LRS", "Standard_LRS"} - - for _, x := range storage_account_types { - config["managed_image_storage_account_type"] = x - _, _, err := newConfig(config, getPackerConfiguration()) - if err != nil { - t.Fatalf("expected config to accept a managed_image_storage_account_type of %q", x) - } - } -} - -func TestConfigShouldAcceptDiskCachingTypes(t *testing.T) { - config := map[string]interface{}{ - "custom_managed_image_resource_group_name": "ignore", - "custom_managed_image_name": "ignore", - "location": "ignore", - "subscription_id": "ignore", - "communicator": "none", - "managed_image_resource_group_name": "ignore", - "managed_image_name": "ignore", - "lab_resource_group_name": "ignore", - "lab_virtual_network_name": "ignore", - - // Does not matter for this test case, just pick one. - "os_type": constants.Target_Linux, - } - - storage_account_types := []string{"None", "ReadOnly", "ReadWrite"} - - for _, x := range storage_account_types { - config["disk_caching_type"] = x - _, _, err := newConfig(config, getPackerConfiguration()) - if err != nil { - t.Fatalf("expected config to accept a disk_caching_type of %q", x) - } - } -} - -func TestConfigAdditionalDiskDefaultIsNil(t *testing.T) { - c, _, _ := newConfig(getDtlBuilderConfiguration(), getPackerConfiguration()) - if c.AdditionalDiskSize != nil { - t.Errorf("Expected Config to not have a set of additional disks, but got a non nil value") - } -} - -func TestConfigAdditionalDiskOverrideDefault(t *testing.T) { - config := map[string]string{ - "capture_name_prefix": "ignore", - "capture_container_name": "ignore", - "location": "ignore", - "image_url": "ignore", - "subscription_id": "ignore", - "os_type": constants.Target_Linux, - "communicator": "none", - "lab_resource_group_name": "ignore", - "lab_virtual_network_name": "ignore", - } - - diskconfig := map[string][]int32{ - "disk_additional_size": {32, 64}, - } - - c, _, _ := newConfig(config, diskconfig, getPackerConfiguration()) - if c.AdditionalDiskSize == nil { - t.Errorf("Expected Config to have a set of additional disks, but got nil") - } - if len(c.AdditionalDiskSize) != 2 { - t.Errorf("Expected Config to have a 2 additional disks, but got %d additional disks", len(c.AdditionalDiskSize)) - } - if c.AdditionalDiskSize[0] != 32 { - t.Errorf("Expected Config to have the first additional disks of size 32Gb, but got %dGb", c.AdditionalDiskSize[0]) - } - if c.AdditionalDiskSize[1] != 64 { - t.Errorf("Expected Config to have the second additional disks of size 64Gb, but got %dGb", c.AdditionalDiskSize[1]) - } -} - -func TestConfigShouldAllowSharedImageGalleryOptions(t *testing.T) { - config := map[string]interface{}{ - "location": "ignore", - "subscription_id": "ignore", - "os_type": "linux", - "lab_resource_group_name": "ignore", - "lab_virtual_network_name": "ignore", - "shared_image_gallery": map[string]string{ - "subscription": "ignore", - "resource_group": "ignore", - "gallery_name": "ignore", - "image_name": "ignore", - "image_version": "ignore", - }, - } - - _, _, err := newConfig(config, getPackerConfiguration()) - if err == nil { - t.Log("expected config to accept Shared Image Gallery options", err) - } - -} - -func TestConfigShouldRejectSharedImageGalleryWithVhdTarget(t *testing.T) { - config := map[string]interface{}{ - "location": "ignore", - "subscription_id": "ignore", - "os_type": "linux", - "shared_image_gallery": map[string]string{ - "subscription": "ignore", - "resource_group": "ignore", - "gallery_name": "ignore", - "image_name": "ignore", - "image_version": "ignore", - }, - "capture_container_name": "ignore", - "capture_name_prefix": "ignore", - "lab_resource_group_name": "ignore", - "lab_virtual_network_name": "ignore", - } - - _, _, err := newConfig(config, getPackerConfiguration()) - if err != nil { - t.Log("expected an error if Shared Image Gallery source is used with VHD target", err) - } -} - -func getDtlBuilderConfiguration() map[string]string { - m := make(map[string]string) - for _, v := range requiredConfigValues { - m[v] = "ignored00" - } - - m["communicator"] = "none" - m["os_type"] = constants.Target_Linux - return m -} - -func getPackerConfiguration() interface{} { - config := map[string]interface{}{ - "packer_build_name": "azure-arm-vm", - "packer_builder_type": "azure-arm-vm", - "packer_debug": "false", - "packer_force": "false", - "packer_template_path": "/home/jenkins/azure-arm-vm/template.json", - } - - return config -} - -func getPackerCommunicatorConfiguration() map[string]string { - config := map[string]string{ - "ssh_timeout": "1h", - "winrm_timeout": "2h", - } - - return config -} diff --git a/builder/azure/dtl/inspector.go b/builder/azure/dtl/inspector.go deleted file mode 100644 index 824963f75..000000000 --- a/builder/azure/dtl/inspector.go +++ /dev/null @@ -1,71 +0,0 @@ -package dtl - -import ( - "bytes" - "io/ioutil" - "log" - "net/http" - - "io" - - "github.com/Azure/go-autorest/autorest" - "github.com/Azure/go-autorest/autorest/azure" - "github.com/hashicorp/packer/builder/azure/common/logutil" -) - -func chop(data []byte, maxlen int64) string { - s := string(data) - if int64(len(s)) > maxlen { - s = s[:maxlen] + "..." - } - return s -} - -func handleBody(body io.ReadCloser, maxlen int64) (io.ReadCloser, string) { - if body == nil { - return nil, "" - } - - defer body.Close() - - b, err := ioutil.ReadAll(body) - if err != nil { - return nil, "" - } - - return ioutil.NopCloser(bytes.NewReader(b)), chop(b, maxlen) -} - -func withInspection(maxlen int64) autorest.PrepareDecorator { - return func(p autorest.Preparer) autorest.Preparer { - return autorest.PreparerFunc(func(r *http.Request) (*http.Request, error) { - body, bodyString := handleBody(r.Body, maxlen) - r.Body = body - - log.Print("Azure request", logutil.Fields{ - "method": r.Method, - "request": r.URL.String(), - "body": bodyString, - }) - return p.Prepare(r) - }) - } -} - -func byInspecting(maxlen int64) autorest.RespondDecorator { - return func(r autorest.Responder) autorest.Responder { - return autorest.ResponderFunc(func(resp *http.Response) error { - body, bodyString := handleBody(resp.Body, maxlen) - resp.Body = body - - log.Print("Azure response", logutil.Fields{ - "status": resp.Status, - "method": resp.Request.Method, - "request": resp.Request.URL.String(), - "x-ms-request-id": azure.ExtractRequestID(resp), - "body": bodyString, - }) - return r.Respond(resp) - }) - } -} diff --git a/builder/azure/dtl/openssh_key_pair.go b/builder/azure/dtl/openssh_key_pair.go deleted file mode 100644 index 3e2f5a371..000000000 --- a/builder/azure/dtl/openssh_key_pair.go +++ /dev/null @@ -1,59 +0,0 @@ -package dtl - -import ( - "crypto/rand" - "crypto/rsa" - "crypto/x509" - "encoding/base64" - "encoding/pem" - "fmt" - "time" - - "golang.org/x/crypto/ssh" -) - -const ( - KeySize = 2048 -) - -type OpenSshKeyPair struct { - privateKey *rsa.PrivateKey - publicKey ssh.PublicKey -} - -func NewOpenSshKeyPair() (*OpenSshKeyPair, error) { - return NewOpenSshKeyPairWithSize(KeySize) -} - -func NewOpenSshKeyPairWithSize(keySize int) (*OpenSshKeyPair, error) { - privateKey, err := rsa.GenerateKey(rand.Reader, keySize) - if err != nil { - return nil, err - } - - publicKey, err := ssh.NewPublicKey(&privateKey.PublicKey) - if err != nil { - return nil, err - } - - return &OpenSshKeyPair{ - privateKey: privateKey, - publicKey: publicKey, - }, nil -} - -func (s *OpenSshKeyPair) AuthorizedKey() string { - return fmt.Sprintf("%s %s packer Azure Deployment%s", - s.publicKey.Type(), - base64.StdEncoding.EncodeToString(s.publicKey.Marshal()), - time.Now().Format(time.RFC3339)) -} - -func (s *OpenSshKeyPair) PrivateKey() []byte { - privateKey := pem.EncodeToMemory(&pem.Block{ - Type: "RSA PRIVATE KEY", - Bytes: x509.MarshalPKCS1PrivateKey(s.privateKey), - }) - - return privateKey -} diff --git a/builder/azure/dtl/openssh_key_pair_test.go b/builder/azure/dtl/openssh_key_pair_test.go deleted file mode 100644 index d4aa6e94d..000000000 --- a/builder/azure/dtl/openssh_key_pair_test.go +++ /dev/null @@ -1,37 +0,0 @@ -package dtl - -import ( - "testing" - - "golang.org/x/crypto/ssh" -) - -func TestFart(t *testing.T) { - -} - -func TestAuthorizedKeyShouldParse(t *testing.T) { - testSubject, err := NewOpenSshKeyPairWithSize(512) - if err != nil { - t.Fatalf("Failed to create a new OpenSSH key pair, err=%s.", err) - } - - authorizedKey := testSubject.AuthorizedKey() - - _, _, _, _, err = ssh.ParseAuthorizedKey([]byte(authorizedKey)) - if err != nil { - t.Fatalf("Failed to parse the authorized key, err=%s", err) - } -} - -func TestPrivateKeyShouldParse(t *testing.T) { - testSubject, err := NewOpenSshKeyPairWithSize(512) - if err != nil { - t.Fatalf("Failed to create a new OpenSSH key pair, err=%s.", err) - } - - _, err = ssh.ParsePrivateKey(testSubject.PrivateKey()) - if err != nil { - t.Fatalf("Failed to parse the private key, err=%s\n", err) - } -} diff --git a/builder/azure/dtl/resource_resolver.go b/builder/azure/dtl/resource_resolver.go deleted file mode 100644 index d5c486fd8..000000000 --- a/builder/azure/dtl/resource_resolver.go +++ /dev/null @@ -1,141 +0,0 @@ -package dtl - -// Code to resolve resources that are required by the API. These resources -// can most likely be resolved without asking the user, thereby reducing the -// amount of configuration they need to provide. -// -// Resource resolver differs from config retriever because resource resolver -// requires a client to communicate with the Azure API. A config retriever is -// used to determine values without use of a client. - -import ( - "context" - "fmt" - "strings" - - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2018-04-01/compute" -) - -type resourceResolver struct { - client *AzureClient - findVirtualNetworkResourceGroup func(*AzureClient, string) (string, error) - findVirtualNetworkSubnet func(*AzureClient, string, string) (string, error) -} - -func newResourceResolver(client *AzureClient) *resourceResolver { - return &resourceResolver{ - client: client, - findVirtualNetworkResourceGroup: findVirtualNetworkResourceGroup, - findVirtualNetworkSubnet: findVirtualNetworkSubnet, - } -} - -func (s *resourceResolver) Resolve(c *Config) error { - // if s.shouldResolveResourceGroup(c) { - // resourceGroupName, err := s.findVirtualNetworkResourceGroup(s.client, c.VirtualNetworkName) - // if err != nil { - // return err - // } - - // subnetName, err := s.findVirtualNetworkSubnet(s.client, resourceGroupName, c.VirtualNetworkName) - // if err != nil { - // return err - // } - - // c.VirtualNetworkResourceGroupName = resourceGroupName - // c.VirtualNetworkSubnetName = subnetName - // } - - if s.shouldResolveManagedImageName(c) { - image, err := findManagedImageByName(s.client, c.CustomManagedImageName, c.CustomManagedImageResourceGroupName) - if err != nil { - return err - } - - c.customManagedImageID = *image.ID - } - - return nil -} - -// func (s *resourceResolver) shouldResolveResourceGroup(c *Config) bool { -// return c.VirtualNetworkName != "" && c.VirtualNetworkResourceGroupName == "" -// } - -func (s *resourceResolver) shouldResolveManagedImageName(c *Config) bool { - return c.CustomManagedImageName != "" -} - -func getResourceGroupNameFromId(id string) string { - // "/subscriptions/3f499422-dd76-4114-8859-86d526c9deb6/resourceGroups/packer-Resource-Group-yylnwsl30j/providers/... - xs := strings.Split(id, "/") - return xs[4] -} - -func findManagedImageByName(client *AzureClient, name, resourceGroupName string) (*compute.Image, error) { - images, err := client.ImagesClient.ListByResourceGroupComplete(context.TODO(), resourceGroupName) - if err != nil { - return nil, err - } - - for images.NotDone() { - image := images.Value() - if strings.EqualFold(name, *image.Name) { - return &image, nil - } - if err = images.Next(); err != nil { - return nil, err - } - } - - return nil, fmt.Errorf("Cannot find an image named '%s' in the resource group '%s'", name, resourceGroupName) -} - -func findVirtualNetworkResourceGroup(client *AzureClient, name string) (string, error) { - virtualNetworks, err := client.VirtualNetworksClient.ListAllComplete(context.TODO()) - if err != nil { - return "", err - } - - resourceGroupNames := make([]string, 0) - for virtualNetworks.NotDone() { - virtualNetwork := virtualNetworks.Value() - if strings.EqualFold(name, *virtualNetwork.Name) { - rgn := getResourceGroupNameFromId(*virtualNetwork.ID) - resourceGroupNames = append(resourceGroupNames, rgn) - } - if err = virtualNetworks.Next(); err != nil { - return "", err - } - } - - if len(resourceGroupNames) == 0 { - return "", fmt.Errorf("Cannot find a resource group with a virtual network called %q", name) - } - - if len(resourceGroupNames) > 1 { - return "", fmt.Errorf("Found multiple resource groups with a virtual network called %q, please use virtual_network_subnet_name and virtual_network_resource_group_name to disambiguate", name) - } - - return resourceGroupNames[0], nil -} - -func findVirtualNetworkSubnet(client *AzureClient, resourceGroupName string, name string) (string, error) { - subnets, err := client.SubnetsClient.List(context.TODO(), resourceGroupName, name) - if err != nil { - return "", err - } - - subnetList := subnets.Values() // only first page of subnets, but only interested in ==0 or >1 - - if len(subnetList) == 0 { - return "", fmt.Errorf("Cannot find a subnet in the resource group %q associated with the virtual network called %q", resourceGroupName, name) - } - - if len(subnetList) > 1 { - return "", fmt.Errorf("Found multiple subnets in the resource group %q associated with the virtual network called %q, please use virtual_network_subnet_name and virtual_network_resource_group_name to disambiguate", resourceGroupName, name) - } - - subnet := subnetList[0] - return *subnet.Name, nil -} diff --git a/builder/azure/dtl/step.go b/builder/azure/dtl/step.go deleted file mode 100644 index b5de4cb95..000000000 --- a/builder/azure/dtl/step.go +++ /dev/null @@ -1,20 +0,0 @@ -package dtl - -import ( - "github.com/hashicorp/packer-plugin-sdk/multistep" - "github.com/hashicorp/packer/builder/azure/common/constants" -) - -func processStepResult( - err error, sayError func(error), state multistep.StateBag) multistep.StepAction { - - if err != nil { - state.Put(constants.Error, err) - sayError(err) - - return multistep.ActionHalt - } - - return multistep.ActionContinue - -} diff --git a/builder/azure/dtl/step_capture_image.go b/builder/azure/dtl/step_capture_image.go deleted file mode 100644 index a16315977..000000000 --- a/builder/azure/dtl/step_capture_image.go +++ /dev/null @@ -1,125 +0,0 @@ -package dtl - -import ( - "context" - "fmt" - - "github.com/Azure/azure-sdk-for-go/services/devtestlabs/mgmt/2018-09-15/dtl" - - "github.com/hashicorp/packer-plugin-sdk/multistep" - packersdk "github.com/hashicorp/packer-plugin-sdk/packer" - "github.com/hashicorp/packer/builder/azure/common/constants" -) - -type StepCaptureImage struct { - client *AzureClient - captureManagedImage func(ctx context.Context) error - get func(client *AzureClient) *CaptureTemplate - config *Config - say func(message string) - error func(e error) -} - -func NewStepCaptureImage(client *AzureClient, ui packersdk.Ui, config *Config) *StepCaptureImage { - var step = &StepCaptureImage{ - client: client, - get: func(client *AzureClient) *CaptureTemplate { - return client.Template - }, - config: config, - say: func(message string) { - ui.Say(message) - }, - error: func(e error) { - ui.Error(e.Error()) - }, - } - - // step.captureVhd = step.captureImage - step.captureManagedImage = step.captureImageFromVM - - return step -} - -func (s *StepCaptureImage) captureImageFromVM(ctx context.Context) error { - imageID := fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.DevTestLab/labs/%s/virtualMachines/%s", - s.config.ClientConfig.SubscriptionID, - s.config.tmpResourceGroupName, - s.config.LabName, - s.config.tmpComputeName) - - customImageProperties := dtl.CustomImageProperties{} - - if s.config.OSType == constants.Target_Linux { - customImageProperties = dtl.CustomImageProperties{ - VM: &dtl.CustomImagePropertiesFromVM{ - LinuxOsInfo: &dtl.LinuxOsInfo{ - LinuxOsState: dtl.DeprovisionRequested, - }, - SourceVMID: &imageID, - }, - } - } else if s.config.OSType == constants.Target_Windows { - customImageProperties = dtl.CustomImageProperties{ - VM: &dtl.CustomImagePropertiesFromVM{ - WindowsOsInfo: &dtl.WindowsOsInfo{ - WindowsOsState: dtl.SysprepRequested, - }, - SourceVMID: &imageID, - }, - } - } - - customImage := &dtl.CustomImage{ - Name: &s.config.ManagedImageName, - CustomImageProperties: &customImageProperties, - } - - f, err := s.client.DtlCustomImageClient.CreateOrUpdate(ctx, s.config.LabResourceGroupName, s.config.LabName, s.config.ManagedImageName, *customImage) - if err == nil { - err = f.WaitForCompletionRef(ctx, s.client.DtlCustomImageClient.Client) - } - if err != nil { - s.say("Error from Capture Image") - s.say(s.client.LastError.Error()) - } - - return err -} - -func (s *StepCaptureImage) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { - s.say("Capturing image ...") - - var computeName = state.Get(constants.ArmComputeName).(string) - var location = state.Get(constants.ArmLocation).(string) - var resourceGroupName = state.Get(constants.ArmResourceGroupName).(string) - - s.say(fmt.Sprintf(" -> Compute ResourceGroupName : '%s'", resourceGroupName)) - s.say(fmt.Sprintf(" -> Compute Name : '%s'", computeName)) - s.say(fmt.Sprintf(" -> Compute Location : '%s'", location)) - - err := s.captureImageFromVM(ctx) - - if err != nil { - s.error(err) - state.Put(constants.Error, err) - - return multistep.ActionHalt - } - - // HACK(chrboum): I do not like this. The capture method should be returning this value - // instead having to pass in another lambda. - // - // Having to resort to capturing the template via an inspector is hack, and once I can - // resolve that I can cleanup this code too. See the comments in azure_client.go for more - // details. - // [paulmey]: autorest.Future now has access to the last http.Response, but I'm not sure if - // the body is still accessible. - template := s.get(s.client) - state.Put(constants.ArmCaptureTemplate, template) - - return multistep.ActionContinue -} - -func (*StepCaptureImage) Cleanup(multistep.StateBag) { -} diff --git a/builder/azure/dtl/step_delete_virtual_machine.go b/builder/azure/dtl/step_delete_virtual_machine.go deleted file mode 100644 index ae21a6ed1..000000000 --- a/builder/azure/dtl/step_delete_virtual_machine.go +++ /dev/null @@ -1,61 +0,0 @@ -package dtl - -import ( - "context" - "fmt" - - "github.com/hashicorp/packer-plugin-sdk/multistep" - packersdk "github.com/hashicorp/packer-plugin-sdk/packer" - "github.com/hashicorp/packer/builder/azure/common/constants" -) - -type StepDeleteVirtualMachine struct { - client *AzureClient - config *Config - delete func(ctx context.Context, resourceGroupName string, computeName string, state multistep.StateBag) error - say func(message string) - error func(e error) -} - -func NewStepDeleteVirtualMachine(client *AzureClient, ui packersdk.Ui, config *Config) *StepDeleteVirtualMachine { - var step = &StepDeleteVirtualMachine{ - client: client, - config: config, - say: func(message string) { ui.Say(message) }, - error: func(e error) { ui.Error(e.Error()) }, - } - - step.delete = step.deleteVirtualMachine - return step -} - -func (s *StepDeleteVirtualMachine) deleteVirtualMachine(ctx context.Context, resourceGroupName string, vmName string, state multistep.StateBag) error { - f, err := s.client.DtlVirtualMachineClient.Delete(ctx, resourceGroupName, s.config.LabName, vmName) - if err == nil { - err = f.WaitForCompletionRef(ctx, s.client.DtlVirtualMachineClient.Client) - } - if err != nil { - s.say("Error from delete VM") - s.say(s.client.LastError.Error()) - } - - return err -} - -func (s *StepDeleteVirtualMachine) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { - s.say("Deleting the virtual machine ...") - - var resourceGroupName = state.Get(constants.ArmResourceGroupName).(string) - var computeName = state.Get(constants.ArmComputeName).(string) - - s.say(fmt.Sprintf(" -> ResourceGroupName : '%s'", resourceGroupName)) - s.say(fmt.Sprintf(" -> ComputeName : '%s'", computeName)) - - err := s.deleteVirtualMachine(ctx, resourceGroupName, computeName, state) - - s.say("Deleting virtual machine ...Complete") - return processStepResult(err, s.error, state) -} - -func (*StepDeleteVirtualMachine) Cleanup(multistep.StateBag) { -} diff --git a/builder/azure/dtl/step_deploy_template.go b/builder/azure/dtl/step_deploy_template.go deleted file mode 100644 index b6dd83589..000000000 --- a/builder/azure/dtl/step_deploy_template.go +++ /dev/null @@ -1,192 +0,0 @@ -package dtl - -import ( - "context" - "errors" - "fmt" - "net/url" - "strings" - - "github.com/hashicorp/packer-plugin-sdk/multistep" - packersdk "github.com/hashicorp/packer-plugin-sdk/packer" - "github.com/hashicorp/packer/builder/azure/common/constants" -) - -type StepDeployTemplate struct { - client *AzureClient - deploy func(ctx context.Context, resourceGroupName string, deploymentName string, state multistep.StateBag) error - delete func(ctx context.Context, client *AzureClient, resourceType string, resourceName string, resourceGroupName string) error - disk func(ctx context.Context, resourceGroupName string, computeName string) (string, string, error) - deleteDisk func(ctx context.Context, imageType string, imageName string, resourceGroupName string) error - say func(message string) - error func(e error) - config *Config - factory templateFactoryFuncDtl - name string -} - -func NewStepDeployTemplate(client *AzureClient, ui packersdk.Ui, config *Config, deploymentName string, factory templateFactoryFuncDtl) *StepDeployTemplate { - var step = &StepDeployTemplate{ - client: client, - say: func(message string) { ui.Say(message) }, - error: func(e error) { ui.Error(e.Error()) }, - config: config, - factory: factory, - name: deploymentName, - } - - step.deploy = step.deployTemplate - step.delete = deleteResource - step.disk = step.getImageDetails - step.deleteDisk = step.deleteImage - return step -} - -func (s *StepDeployTemplate) deployTemplate(ctx context.Context, resourceGroupName string, deploymentName string, state multistep.StateBag) error { - - vmlistPage, err := s.client.DtlVirtualMachineClient.List(ctx, s.config.tmpResourceGroupName, s.config.LabName, "", "", nil, "") - - if err != nil { - s.say(s.client.LastError.Error()) - return err - } - - vmList := vmlistPage.Values() - for i := range vmList { - if *vmList[i].Name == s.config.tmpComputeName { - return fmt.Errorf("Error: Virtual Machine %s already exists. Please use another name", s.config.tmpComputeName) - } - } - - s.say(fmt.Sprintf("Creating Virtual Machine %s", s.config.tmpComputeName)) - labMachine, err := s.factory(s.config) - if err != nil { - return err - } - - f, err := s.client.DtlLabsClient.CreateEnvironment(ctx, s.config.tmpResourceGroupName, s.config.LabName, *labMachine) - - if err == nil { - err = f.WaitForCompletionRef(ctx, s.client.DtlLabsClient.Client) - } - if err != nil { - s.say(s.client.LastError.Error()) - return err - } - expand := "Properties($expand=ComputeVm,Artifacts,NetworkInterface)" - - vm, err := s.client.DtlVirtualMachineClient.Get(ctx, s.config.tmpResourceGroupName, s.config.LabName, s.config.tmpComputeName, expand) - if err != nil { - s.say(s.client.LastError.Error()) - } - xs := strings.Split(*vm.LabVirtualMachineProperties.ComputeID, "/") - s.config.VMCreationResourceGroup = xs[4] - - s.say(fmt.Sprintf(" -> VM FQDN : '%s'", *vm.Fqdn)) - - state.Put(constants.SSHHost, *vm.Fqdn) - s.config.tmpFQDN = *vm.Fqdn - - // Resuing the Resource group name from common constants as all steps depend on it. - state.Put(constants.ArmResourceGroupName, s.config.VMCreationResourceGroup) - - s.say(fmt.Sprintf(" -> VM ResourceGroupName : '%s'", s.config.VMCreationResourceGroup)) - - return err -} - -func (s *StepDeployTemplate) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { - s.say("Deploying deployment template ...") - - var resourceGroupName = state.Get(constants.ArmResourceGroupName).(string) - - s.say(fmt.Sprintf(" -> Lab ResourceGroupName : '%s'", resourceGroupName)) - - return processStepResult( - s.deploy(ctx, resourceGroupName, s.name, state), - s.error, state) -} - -func (s *StepDeployTemplate) getImageDetails(ctx context.Context, resourceGroupName string, computeName string) (string, string, error) { - //We can't depend on constants.ArmOSDiskVhd being set - var imageName string - var imageType string - vm, err := s.client.VirtualMachinesClient.Get(ctx, resourceGroupName, computeName, "") - if err != nil { - return imageName, imageType, err - } else { - if vm.StorageProfile.OsDisk.Vhd != nil { - imageType = "image" - imageName = *vm.StorageProfile.OsDisk.Vhd.URI - } else { - imageType = "Microsoft.Compute/disks" - imageName = *vm.StorageProfile.OsDisk.ManagedDisk.ID - } - } - return imageType, imageName, nil -} - -//TODO(paulmey): move to helpers file -func deleteResource(ctx context.Context, client *AzureClient, resourceType string, resourceName string, resourceGroupName string) error { - switch resourceType { - case "Microsoft.Compute/virtualMachines": - f, err := client.VirtualMachinesClient.Delete(ctx, resourceGroupName, resourceName) - if err == nil { - err = f.WaitForCompletionRef(ctx, client.VirtualMachinesClient.Client) - } - return err - case "Microsoft.Network/networkInterfaces": - f, err := client.InterfacesClient.Delete(ctx, resourceGroupName, resourceName) - if err == nil { - err = f.WaitForCompletionRef(ctx, client.InterfacesClient.Client) - } - return err - case "Microsoft.Network/virtualNetworks": - f, err := client.VirtualNetworksClient.Delete(ctx, resourceGroupName, resourceName) - if err == nil { - err = f.WaitForCompletionRef(ctx, client.VirtualNetworksClient.Client) - } - return err - case "Microsoft.Network/publicIPAddresses": - f, err := client.PublicIPAddressesClient.Delete(ctx, resourceGroupName, resourceName) - if err == nil { - err = f.WaitForCompletionRef(ctx, client.PublicIPAddressesClient.Client) - } - return err - } - return nil -} - -func (s *StepDeployTemplate) deleteImage(ctx context.Context, imageType string, imageName string, resourceGroupName string) error { - // Managed disk - if imageType == "Microsoft.Compute/disks" { - xs := strings.Split(imageName, "/") - diskName := xs[len(xs)-1] - f, err := s.client.DisksClient.Delete(ctx, resourceGroupName, diskName) - if err == nil { - err = f.WaitForCompletionRef(ctx, s.client.DisksClient.Client) - } - return err - } - // VHD image - u, err := url.Parse(imageName) - if err != nil { - return err - } - xs := strings.Split(u.Path, "/") - if len(xs) < 3 { - return errors.New("Unable to parse path of image " + imageName) - } - var storageAccountName = xs[1] - var blobName = strings.Join(xs[2:], "/") - - blob := s.client.BlobStorageClient.GetContainerReference(storageAccountName).GetBlobReference(blobName) - err = blob.Delete(nil) - return err -} - -func (s *StepDeployTemplate) Cleanup(state multistep.StateBag) { - //Only clean up if this was an existing resource group and the resource group - //is marked as created - // Just return now -} diff --git a/builder/azure/dtl/step_power_off_compute.go b/builder/azure/dtl/step_power_off_compute.go deleted file mode 100644 index f0eed13d8..000000000 --- a/builder/azure/dtl/step_power_off_compute.go +++ /dev/null @@ -1,61 +0,0 @@ -package dtl - -import ( - "context" - "fmt" - - "github.com/hashicorp/packer-plugin-sdk/multistep" - packersdk "github.com/hashicorp/packer-plugin-sdk/packer" - "github.com/hashicorp/packer/builder/azure/common/constants" -) - -type StepPowerOffCompute struct { - client *AzureClient - config *Config - powerOff func(ctx context.Context, resourceGroupName string, labName, computeName string) error - say func(message string) - error func(e error) -} - -func NewStepPowerOffCompute(client *AzureClient, ui packersdk.Ui, config *Config) *StepPowerOffCompute { - - var step = &StepPowerOffCompute{ - client: client, - config: config, - say: func(message string) { ui.Say(message) }, - error: func(e error) { ui.Error(e.Error()) }, - } - - step.powerOff = step.powerOffCompute - return step -} - -func (s *StepPowerOffCompute) powerOffCompute(ctx context.Context, resourceGroupName string, labName, computeName string) error { - //f, err := s.client.VirtualMachinesClient.Deallocate(ctx, resourceGroupName, computeName) - f, err := s.client.DtlVirtualMachineClient.Stop(ctx, resourceGroupName, labName, computeName) - if err == nil { - err = f.WaitForCompletionRef(ctx, s.client.DtlVirtualMachineClient.Client) - } - if err != nil { - s.say(s.client.LastError.Error()) - } - return err -} - -func (s *StepPowerOffCompute) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { - s.say("Powering off machine ...") - - var resourceGroupName = state.Get(constants.ArmResourceGroupName).(string) - var computeName = state.Get(constants.ArmComputeName).(string) - - s.say(fmt.Sprintf(" -> ResourceGroupName : '%s'", resourceGroupName)) - s.say(fmt.Sprintf(" -> ComputeName : '%s'", computeName)) - - err := s.powerOff(ctx, s.config.LabResourceGroupName, s.config.LabName, computeName) - - s.say("Powering off machine ...Complete") - return processStepResult(err, s.error, state) -} - -func (*StepPowerOffCompute) Cleanup(multistep.StateBag) { -} diff --git a/builder/azure/dtl/step_publish_to_shared_image_gallery.go b/builder/azure/dtl/step_publish_to_shared_image_gallery.go deleted file mode 100644 index f080a0dde..000000000 --- a/builder/azure/dtl/step_publish_to_shared_image_gallery.go +++ /dev/null @@ -1,126 +0,0 @@ -package dtl - -import ( - "context" - "fmt" - - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-03-01/compute" - "github.com/hashicorp/packer-plugin-sdk/multistep" - packersdk "github.com/hashicorp/packer-plugin-sdk/packer" - "github.com/hashicorp/packer/builder/azure/common/constants" -) - -type StepPublishToSharedImageGallery struct { - client *AzureClient - publish func(ctx context.Context, mdiID, miSigPubRg, miSIGalleryName, miSGImageName, miSGImageVersion string, miSigReplicationRegions []string, location string, tags map[string]*string) (string, error) - say func(message string) - error func(e error) - toSIG func() bool -} - -func NewStepPublishToSharedImageGallery(client *AzureClient, ui packersdk.Ui, config *Config) *StepPublishToSharedImageGallery { - var step = &StepPublishToSharedImageGallery{ - client: client, - say: func(message string) { - ui.Say(message) - }, - error: func(e error) { - ui.Error(e.Error()) - }, - toSIG: func() bool { - return config.isManagedImage() && config.SharedGalleryDestination.SigDestinationGalleryName != "" - }, - } - - step.publish = step.publishToSig - return step -} - -func (s *StepPublishToSharedImageGallery) publishToSig(ctx context.Context, mdiID string, miSigPubRg string, miSIGalleryName string, miSGImageName string, miSGImageVersion string, miSigReplicationRegions []string, location string, tags map[string]*string) (string, error) { - - replicationRegions := make([]compute.TargetRegion, len(miSigReplicationRegions)) - for i, v := range miSigReplicationRegions { - regionName := v - replicationRegions[i] = compute.TargetRegion{Name: ®ionName} - } - - galleryImageVersion := compute.GalleryImageVersion{ - Location: &location, - Tags: tags, - GalleryImageVersionProperties: &compute.GalleryImageVersionProperties{ - PublishingProfile: &compute.GalleryImageVersionPublishingProfile{ - Source: &compute.GalleryArtifactSource{ - ManagedImage: &compute.ManagedArtifact{ - ID: &mdiID, - }, - }, - TargetRegions: &replicationRegions, - }, - }, - } - - f, err := s.client.GalleryImageVersionsClient.CreateOrUpdate(ctx, miSigPubRg, miSIGalleryName, miSGImageName, miSGImageVersion, galleryImageVersion) - - if err != nil { - s.say(s.client.LastError.Error()) - return "", err - } - - err = f.WaitForCompletionRef(ctx, s.client.GalleryImageVersionsClient.Client) - - if err != nil { - s.say(s.client.LastError.Error()) - return "", err - } - - createdSGImageVersion, err := f.Result(s.client.GalleryImageVersionsClient) - - if err != nil { - s.say(s.client.LastError.Error()) - return "", err - } - - s.say(fmt.Sprintf(" -> Shared Gallery Image Version ID : '%s'", *(createdSGImageVersion.ID))) - return *(createdSGImageVersion.ID), nil -} - -func (s *StepPublishToSharedImageGallery) Run(ctx context.Context, stateBag multistep.StateBag) multistep.StepAction { - if !s.toSIG() { - return multistep.ActionContinue - } - - s.say("Publishing to Shared Image Gallery ...") - - var miSigPubRg = stateBag.Get(constants.ArmManagedImageSigPublishResourceGroup).(string) - var miSIGalleryName = stateBag.Get(constants.ArmManagedImageSharedGalleryName).(string) - var miSGImageName = stateBag.Get(constants.ArmManagedImageSharedGalleryImageName).(string) - var miSGImageVersion = stateBag.Get(constants.ArmManagedImageSharedGalleryImageVersion).(string) - var location = stateBag.Get(constants.ArmLocation).(string) - var tags = stateBag.Get(constants.ArmTags).(map[string]*string) - var miSigReplicationRegions = stateBag.Get(constants.ArmManagedImageSharedGalleryReplicationRegions).([]string) - var targetManagedImageResourceGroupName = stateBag.Get(constants.ArmManagedImageResourceGroupName).(string) - var targetManagedImageName = stateBag.Get(constants.ArmManagedImageName).(string) - var managedImageSubscription = stateBag.Get(constants.ArmManagedImageSubscription).(string) - var mdiID = fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Compute/images/%s", managedImageSubscription, targetManagedImageResourceGroupName, targetManagedImageName) - - s.say(fmt.Sprintf(" -> MDI ID used for SIG publish : '%s'", mdiID)) - s.say(fmt.Sprintf(" -> SIG publish resource group : '%s'", miSigPubRg)) - s.say(fmt.Sprintf(" -> SIG gallery name : '%s'", miSIGalleryName)) - s.say(fmt.Sprintf(" -> SIG image name : '%s'", miSGImageName)) - s.say(fmt.Sprintf(" -> SIG image version : '%s'", miSGImageVersion)) - s.say(fmt.Sprintf(" -> SIG replication regions : '%v'", miSigReplicationRegions)) - createdGalleryImageVersionID, err := s.publish(ctx, mdiID, miSigPubRg, miSIGalleryName, miSGImageName, miSGImageVersion, miSigReplicationRegions, location, tags) - - if err != nil { - stateBag.Put(constants.Error, err) - s.error(err) - - return multistep.ActionHalt - } - - stateBag.Put(constants.ArmManagedImageSharedGalleryId, createdGalleryImageVersionID) - return multistep.ActionContinue -} - -func (*StepPublishToSharedImageGallery) Cleanup(multistep.StateBag) { -} diff --git a/builder/azure/dtl/step_save_winrm_password.go b/builder/azure/dtl/step_save_winrm_password.go deleted file mode 100644 index 975a9ff39..000000000 --- a/builder/azure/dtl/step_save_winrm_password.go +++ /dev/null @@ -1,22 +0,0 @@ -package dtl - -import ( - "context" - - "github.com/hashicorp/packer-plugin-sdk/multistep" - packersdk "github.com/hashicorp/packer-plugin-sdk/packer" -) - -type StepSaveWinRMPassword struct { - Password string - BuildName string -} - -func (s *StepSaveWinRMPassword) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { - // store so that we can access this later during provisioning - state.Put("winrm_password", s.Password) - packersdk.LogSecretFilter.Set(s.Password) - return multistep.ActionContinue -} - -func (s *StepSaveWinRMPassword) Cleanup(multistep.StateBag) {} diff --git a/builder/azure/dtl/step_test.go b/builder/azure/dtl/step_test.go deleted file mode 100644 index d337a5e49..000000000 --- a/builder/azure/dtl/step_test.go +++ /dev/null @@ -1,40 +0,0 @@ -package dtl - -import ( - "fmt" - "testing" - - "github.com/hashicorp/packer-plugin-sdk/multistep" - "github.com/hashicorp/packer/builder/azure/common/constants" -) - -func TestProcessStepResultShouldContinueForNonErrors(t *testing.T) { - stateBag := new(multistep.BasicStateBag) - - code := processStepResult(nil, func(error) { t.Fatal("Should not be called!") }, stateBag) - if _, ok := stateBag.GetOk(constants.Error); ok { - t.Errorf("Error was nil, but was still in the state bag.") - } - - if code != multistep.ActionContinue { - t.Errorf("Expected ActionContinue(%d), but got=%d", multistep.ActionContinue, code) - } -} - -func TestProcessStepResultShouldHaltOnError(t *testing.T) { - stateBag := new(multistep.BasicStateBag) - isSaidError := false - - code := processStepResult(fmt.Errorf("boom"), func(error) { isSaidError = true }, stateBag) - if _, ok := stateBag.GetOk(constants.Error); !ok { - t.Errorf("Error was non nil, but was not in the state bag.") - } - - if !isSaidError { - t.Errorf("Expected error to be said, but it was not.") - } - - if code != multistep.ActionHalt { - t.Errorf("Expected ActionHalt(%d), but got=%d", multistep.ActionHalt, code) - } -} diff --git a/builder/azure/dtl/template_factory.go b/builder/azure/dtl/template_factory.go deleted file mode 100644 index 821d43a03..000000000 --- a/builder/azure/dtl/template_factory.go +++ /dev/null @@ -1,136 +0,0 @@ -package dtl - -import ( - "fmt" - "strings" - - "github.com/Azure/azure-sdk-for-go/services/devtestlabs/mgmt/2018-09-15/dtl" -) - -type templateFactoryFuncDtl func(*Config) (*dtl.LabVirtualMachineCreationParameter, error) - -func newBool(val bool) *bool { - b := true - if val == b { - return &b - } else { - b = false - return &b - } -} - -func getCustomImageId(config *Config) *string { - if config.CustomManagedImageName != "" && config.CustomManagedImageResourceGroupName != "" { - customManagedImageID := fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Compute/images/%s", - config.ClientConfig.SubscriptionID, - config.CustomManagedImageResourceGroupName, - config.CustomManagedImageName) - return &customManagedImageID - } - return nil -} - -func GetVirtualMachineDeployment(config *Config) (*dtl.LabVirtualMachineCreationParameter, error) { - - galleryImageRef := dtl.GalleryImageReference{ - Offer: &config.ImageOffer, - Publisher: &config.ImagePublisher, - Sku: &config.ImageSku, - OsType: &config.OSType, - Version: &config.ImageVersion, - } - - labVirtualNetworkID := fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.DevTestLab/labs/%s/virtualnetworks/%s", - config.ClientConfig.SubscriptionID, - config.tmpResourceGroupName, - config.LabName, - config.LabVirtualNetworkName) - - dtlArtifacts := []dtl.ArtifactInstallProperties{} - - if config.DtlArtifacts != nil { - for i := range config.DtlArtifacts { - if config.DtlArtifacts[i].RepositoryName == "" { - config.DtlArtifacts[i].RepositoryName = "public repo" - } - config.DtlArtifacts[i].ArtifactId = fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.DevTestLab/labs/%s/artifactSources/%s/artifacts/%s", - config.ClientConfig.SubscriptionID, - config.tmpResourceGroupName, - config.LabName, - config.DtlArtifacts[i].RepositoryName, - config.DtlArtifacts[i].ArtifactName) - - dparams := []dtl.ArtifactParameterProperties{} - for j := range config.DtlArtifacts[i].Parameters { - - dp := &dtl.ArtifactParameterProperties{} - dp.Name = &config.DtlArtifacts[i].Parameters[j].Name - dp.Value = &config.DtlArtifacts[i].Parameters[j].Value - - dparams = append(dparams, *dp) - } - dtlArtifact := &dtl.ArtifactInstallProperties{ - ArtifactTitle: &config.DtlArtifacts[i].ArtifactName, - ArtifactID: &config.DtlArtifacts[i].ArtifactId, - Parameters: &dparams, - } - dtlArtifacts = append(dtlArtifacts, *dtlArtifact) - } - } - - if strings.ToLower(config.OSType) == "windows" { - // Add mandatory Artifact - var winrma = "windows-winrm" - var artifactid = fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.DevTestLab/labs/%s/artifactSources/public repo/artifacts/%s", - config.ClientConfig.SubscriptionID, - config.tmpResourceGroupName, - config.LabName, - winrma) - - var hostname = "hostName" - //var hostNameValue = fmt.Sprintf("%s.%s.cloudapp.azure.com", config.VMName, config.Location) - dparams := []dtl.ArtifactParameterProperties{} - dp := &dtl.ArtifactParameterProperties{} - dp.Name = &hostname - dp.Value = &config.tmpFQDN - dparams = append(dparams, *dp) - - winrmArtifact := &dtl.ArtifactInstallProperties{ - ArtifactTitle: &winrma, - ArtifactID: &artifactid, - Parameters: &dparams, - } - dtlArtifacts = append(dtlArtifacts, *winrmArtifact) - } - - labMachineProps := &dtl.LabVirtualMachineCreationParameterProperties{ - CreatedByUserID: &config.ClientConfig.ClientID, - OwnerObjectID: &config.ClientConfig.ObjectID, - OsType: &config.OSType, - Size: &config.VMSize, - UserName: &config.UserName, - Password: &config.Password, - SSHKey: &config.sshAuthorizedKey, - IsAuthenticationWithSSHKey: newBool(true), - LabSubnetName: &config.LabSubnetName, - LabVirtualNetworkID: &labVirtualNetworkID, - DisallowPublicIPAddress: newBool(false), - GalleryImageReference: &galleryImageRef, - CustomImageID: getCustomImageId(config), - PlanID: &config.PlanID, - - AllowClaim: newBool(false), - StorageType: &config.StorageType, - VirtualMachineCreationSource: dtl.FromGalleryImage, - Artifacts: &dtlArtifacts, - } - - labMachine := &dtl.LabVirtualMachineCreationParameter{ - Name: &config.tmpComputeName, - Location: &config.Location, - Tags: config.AzureTags, - LabVirtualMachineCreationParameterProperties: labMachineProps, - } - - return labMachine, nil -} diff --git a/builder/azure/dtl/template_funcs.go b/builder/azure/dtl/template_funcs.go deleted file mode 100644 index d34952e1c..000000000 --- a/builder/azure/dtl/template_funcs.go +++ /dev/null @@ -1,46 +0,0 @@ -package dtl - -import ( - "bytes" - "text/template" - - packertpl "github.com/hashicorp/packer-plugin-sdk/template" -) - -func isValidByteValue(b byte) bool { - if '0' <= b && b <= '9' { - return true - } - if 'a' <= b && b <= 'z' { - return true - } - if 'A' <= b && b <= 'Z' { - return true - } - return b == '.' || b == '_' || b == '-' -} - -// Clean up image name by replacing invalid characters with "-" -// Names are not allowed to end in '.', '-', or '_' and are trimmed. -func templateCleanImageName(s string) string { - if ok, _ := assertManagedImageName(s, ""); ok { - return s - } - b := []byte(s) - newb := make([]byte, len(b)) - for i := range newb { - if isValidByteValue(b[i]) { - newb[i] = b[i] - } else { - newb[i] = '-' - } - } - - newb = bytes.TrimRight(newb, "-_.") - return string(newb) -} - -var TemplateFuncs = template.FuncMap{ - "clean_resource_name": templateCleanImageName, - "clean_image_name": packertpl.DeprecatedTemplateFunc("clean_image_name", "clean_resource_name", templateCleanImageName), -} diff --git a/builder/azure/dtl/template_funcs_test.go b/builder/azure/dtl/template_funcs_test.go deleted file mode 100644 index 13c8e25ce..000000000 --- a/builder/azure/dtl/template_funcs_test.go +++ /dev/null @@ -1,49 +0,0 @@ -package dtl - -import "testing" - -func TestTemplateCleanImageName(t *testing.T) { - vals := []struct { - origName string - expected string - }{ - // test that valid name is unchanged - { - origName: "abcde-012345xyz", - expected: "abcde-012345xyz", - }, - // test that colons are converted to hyphens - { - origName: "abcde-012345v1.0:0", - expected: "abcde-012345v1.0-0", - }, - // Name starting with number is not valid, but not in scope of this - // function to correct - { - origName: "012345v1.0:0", - expected: "012345v1.0-0", - }, - // Name over 80 chars is not valid, but not corrected by this function. - { - origName: "l012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789", - expected: "l012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789", - }, - // Name cannot end in a -Name over 80 chars is not valid, but not corrected by this function. - { - origName: "abcde-:_", - expected: "abcde", - }, - // Lost of special characters - { - origName: "My()./-_:&^ $%[]#'@name", - expected: "My--.--_-----------name", - }, - } - - for _, v := range vals { - name := templateCleanImageName(v.origName) - if name != v.expected { - t.Fatalf("template names do not match: expected %s got %s\n", v.expected, name) - } - } -} diff --git a/builder/azure/dtl/tempname.go b/builder/azure/dtl/tempname.go deleted file mode 100644 index 38a138c39..000000000 --- a/builder/azure/dtl/tempname.go +++ /dev/null @@ -1,74 +0,0 @@ -package dtl - -import ( - "fmt" - "strings" - - "github.com/hashicorp/packer-plugin-sdk/random" -) - -type TempName struct { - AdminPassword string - CertificatePassword string - ComputeName string - DeploymentName string - KeyVaultName string - ResourceGroupName string - OSDiskName string - NicName string - SubnetName string - PublicIPAddressName string - VirtualNetworkName string -} - -func NewTempName(c *Config) *TempName { - tempName := &TempName{} - suffix := random.AlphaNumLower(10) - - if c.VMName != "" { - suffix = c.VMName - } - - tempName.ComputeName = suffix - tempName.DeploymentName = fmt.Sprintf("pkrdp%s", suffix) - tempName.KeyVaultName = fmt.Sprintf("pkrkv%s", suffix) - tempName.OSDiskName = fmt.Sprintf("pkros%s", suffix) - tempName.NicName = tempName.ComputeName - tempName.PublicIPAddressName = tempName.ComputeName - tempName.SubnetName = fmt.Sprintf("pkrsn%s", suffix) - tempName.VirtualNetworkName = fmt.Sprintf("pkrvn%s", suffix) - tempName.ResourceGroupName = fmt.Sprintf("packer-Resource-Group-%s", suffix) - - tempName.AdminPassword = generatePassword() - tempName.CertificatePassword = random.AlphaNum(32) - return tempName -} - -// generate a password that is acceptable to Azure -// Three of the four items must be met. -// 1. Contains an uppercase character -// 2. Contains a lowercase character -// 3. Contains a numeric digit -// 4. Contains a special character -func generatePassword() string { - var s string - for i := 0; i < 100; i++ { - s := random.AlphaNum(32) - if !strings.ContainsAny(s, random.PossibleNumbers) { - continue - } - - if !strings.ContainsAny(s, random.PossibleLowerCase) { - continue - } - - if !strings.ContainsAny(s, random.PossibleUpperCase) { - continue - } - - return s - } - - // if an acceptable password cannot be generated in 100 tries, give up - return s -} diff --git a/builder/azure/examples/centos.json b/builder/azure/examples/centos.json deleted file mode 100644 index 9570ce7e9..000000000 --- a/builder/azure/examples/centos.json +++ /dev/null @@ -1,44 +0,0 @@ -{ - "variables": { - "client_id": "{{env `ARM_CLIENT_ID`}}", - "client_secret": "{{env `ARM_CLIENT_SECRET`}}", - "subscription_id": "{{env `ARM_SUBSCRIPTION_ID`}}", - "tenant_id": "{{env `ARM_TENANT_ID`}}", - "ssh_user": "centos", - "ssh_pass": "{{env `ARM_SSH_PASS`}}" - }, - "builders": [{ - "type": "azure-arm", - - "client_id": "{{user `client_id`}}", - "client_secret": "{{user `client_secret`}}", - "subscription_id": "{{user `subscription_id`}}", - "tenant_id": "{{user `tenant_id`}}", - - "managed_image_resource_group_name": "packertest", - "managed_image_name": "MyCentOSImage", - - "ssh_username": "{{user `ssh_user`}}", - "ssh_password": "{{user `ssh_pass`}}", - - "os_type": "Linux", - "image_publisher": "OpenLogic", - "image_offer": "CentOS", - "image_sku": "7.3", - "image_version": "latest", - "ssh_pty": "true", - - "location": "South Central US", - "vm_size": "Standard_DS2_v2" - }], - "provisioners": [{ - "execute_command": "echo '{{user `ssh_pass`}}' | {{ .Vars }} sudo -S -E sh '{{ .Path }}'", - "inline": [ - "yum update -y", - "/usr/sbin/waagent -force -deprovision+user && export HISTSIZE=0 && sync" - ], - "inline_shebang": "/bin/sh -x", - "type": "shell", - "skip_clean": true - }] -} diff --git a/builder/azure/examples/debian-chroot.json b/builder/azure/examples/debian-chroot.json deleted file mode 100644 index 00909a55f..000000000 --- a/builder/azure/examples/debian-chroot.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "variables": { - "client_id": "{{env `ARM_CLIENT_ID`}}", - "client_secret": "{{env `ARM_CLIENT_SECRET`}}", - "subscription_id": "{{env `ARM_SUBSCRIPTION_ID`}}", - "resource_group": "{{env `ARM_IMAGE_RESOURCEGROUP_ID`}}" - }, - "builders": [{ - "type": "azure-chroot", - - "client_id": "{{user `client_id`}}", - "client_secret": "{{user `client_secret`}}", - "subscription_id": "{{user `subscription_id`}}", - - "image_resource_id": "/subscriptions/{{user `subscription_id`}}/resourceGroups/{{user `resource_group`}}/providers/Microsoft.Compute/images/MyDebianOSImage-{{timestamp}}", - - "source": "credativ:Debian:9:latest" - }], - "provisioners": [{ - "inline": [ - "apt-get update", - "apt-get upgrade -y" - ], - "inline_shebang": "/bin/sh -x", - "type": "shell" - }] -} \ No newline at end of file diff --git a/builder/azure/examples/debian.json b/builder/azure/examples/debian.json deleted file mode 100644 index f1d97daad..000000000 --- a/builder/azure/examples/debian.json +++ /dev/null @@ -1,45 +0,0 @@ -{ - "variables": { - "client_id": "{{env `ARM_CLIENT_ID`}}", - "client_secret": "{{env `ARM_CLIENT_SECRET`}}", - "subscription_id": "{{env `ARM_SUBSCRIPTION_ID`}}", - "ssh_user": "packer", - "ssh_pass": "{{env `ARM_SSH_PASS`}}" - }, - "builders": [{ - "type": "azure-arm", - - "client_id": "{{user `client_id`}}", - "client_secret": "{{user `client_secret`}}", - "resource_group_name": "{{user `resource_group`}}", - "storage_account": "{{user `storage_account`}}", - "subscription_id": "{{user `subscription_id`}}", - - "managed_image_resource_group_name": "packertest", - "managed_image_name": "MyDebianOSImage", - - "ssh_username": "{{user `ssh_user`}}", - "ssh_password": "{{user `ssh_pass`}}", - - "os_type": "Linux", - "image_publisher": "credativ", - "image_offer": "Debian", - "image_sku": "9", - "ssh_pty": "true", - - "location": "South Central US", - "vm_size": "Standard_DS2_v2" - }], - "provisioners": [{ - "execute_command": "echo '{{user `ssh_pass`}}' | {{ .Vars }} sudo -S -E sh '{{ .Path }}'", - "inline": [ - "apt-get update", - "apt-get upgrade -y", - - "/usr/sbin/waagent -force -deprovision+user && export HISTSIZE=0 && sync" - ], - "inline_shebang": "/bin/sh -x", - "skip_clean": true, - "type": "shell" - }] -} diff --git a/builder/azure/examples/freebsd-chroot.json b/builder/azure/examples/freebsd-chroot.json deleted file mode 100644 index 8b8d42825..000000000 --- a/builder/azure/examples/freebsd-chroot.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "variables": {}, - "builders": [ - { - "type": "azure-chroot", - "source": "thefreebsdfoundation:freebsd-12_1:12_1-release:latest", - "image_resource_id": "/subscriptions/{{vm `subscription_id`}}/resourceGroups/{{vm `resource_group`}}/providers/Microsoft.Compute/images/freebsd-{{timestamp}}", - "os_disk_size_gb": 64, - "os_disk_storage_account_type": "Premium_LRS", - "mount_partition": 2, - "chroot_mounts": [ - ["devfs", "devfs", "/dev"], - ["procfs", "procfs", "/proc"] - ] - } - ], - "provisioners": [ - { - "inline": [ - "env ASSUME_ALWAYS_YES=YES pkg bootstrap" - ], - "inline_shebang": "/bin/sh -x", - "type": "shell" - } - ] -} diff --git a/builder/azure/examples/freebsd.json b/builder/azure/examples/freebsd.json deleted file mode 100644 index eedad52ea..000000000 --- a/builder/azure/examples/freebsd.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "variables": { - "client_id": "{{env `ARM_CLIENT_ID`}}", - "client_secret": "{{env `ARM_CLIENT_SECRET`}}", - "subscription_id": "{{env `ARM_SUBSCRIPTION_ID`}}", - "ssh_user": "packer", - "ssh_pass": "{{env `ARM_SSH_PASS`}}" - }, - "builders": [{ - "type": "azure-arm", - - "client_id": "{{user `client_id`}}", - "client_secret": "{{user `client_secret`}}", - "subscription_id": "{{user `subscription_id`}}", - - "managed_image_resource_group_name": "packertest", - "managed_image_name": "MyFreeBsdOSImage", - - "ssh_username": "{{user `ssh_user`}}", - "ssh_password": "{{user `ssh_pass`}}", - - "os_type": "Linux", - "image_publisher": "MicrosoftOSTC", - "image_offer": "FreeBSD", - "image_sku": "11.1", - "image_version": "latest", - - "location": "West US 2", - "vm_size": "Standard_DS2_v2" - }], - "provisioners": [{ - "execute_command": "chmod +x {{ .Path }}; {{ .Vars }} sudo -E sh '{{ .Path }}'", - "inline": [ - "env ASSUME_ALWAYS_YES=YES pkg bootstrap", - "/usr/sbin/waagent -force -deprovision+user && export HISTSIZE=0 && sync" - ], - "inline_shebang": "/bin/sh -x", - "type": "shell", - "skip_clean": "true", - "expect_disconnect": "true" - }] - -} diff --git a/builder/azure/examples/linux_custom_image.json b/builder/azure/examples/linux_custom_image.json deleted file mode 100644 index 91bbf0a39..000000000 --- a/builder/azure/examples/linux_custom_image.json +++ /dev/null @@ -1,44 +0,0 @@ -{ - "variables": { - "client_id": "{{env `ARM_CLIENT_ID`}}", - "client_secret": "{{env `ARM_CLIENT_SECRET`}}", - "resource_group": "{{env `ARM_RESOURCE_GROUP`}}", - "storage_account": "{{env `ARM_STORAGE_ACCOUNT`}}", - "subscription_id": "{{env `ARM_SUBSCRIPTION_ID`}}" - }, - "builders": [ - { - "type": "azure-arm", - - "client_id": "{{user `client_id`}}", - "client_secret": "{{user `client_secret`}}", - "resource_group_name": "{{user `resource_group`}}", - "storage_account": "{{user `storage_account`}}", - "subscription_id": "{{user `subscription_id`}}", - - "capture_container_name": "images", - "capture_name_prefix": "packer", - - "os_type": "Linux", - "image_url": "https://my-storage-account.blob.core.windows.net/path/to/your/custom/image.vhd", - - "azure_tags": { - "dept": "engineering", - "task": "image deployment" - }, - - "location": "West US", - "vm_size": "Standard_DS2_v2" - } - ], - "provisioners": [{ - "execute_command": "chmod +x {{ .Path }}; {{ .Vars }} sudo -E sh '{{ .Path }}'", - "inline": [ - "apt-get update", - "apt-get upgrade -y", - "/usr/sbin/waagent -force -deprovision+user && export HISTSIZE=0 && sync" - ], - "inline_shebang": "/bin/sh -x", - "type": "shell" - }] -} diff --git a/builder/azure/examples/linux_custom_managed_image.json b/builder/azure/examples/linux_custom_managed_image.json deleted file mode 100644 index 7863e30c6..000000000 --- a/builder/azure/examples/linux_custom_managed_image.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "variables": { - "client_id": "{{env `ARM_CLIENT_ID`}}", - "client_secret": "{{env `ARM_CLIENT_SECRET`}}", - "subscription_id": "{{env `ARM_SUBSCRIPTION_ID`}}" - }, - "builders": [ - { - "type": "azure-arm", - - "client_id": "{{user `client_id`}}", - "client_secret": "{{user `client_secret`}}", - "subscription_id": "{{user `subscription_id`}}", - - "os_type": "Linux", - "custom_managed_image_resource_group_name": "MyResourceGroup", - "custom_managed_image_name": "MyImage", - "managed_image_resource_group_name": "PackerImages", - "managed_image_name": "MyImage", - - "azure_tags": { - "dept": "engineering", - "task": "image deployment" - }, - - "location": "West US", - "vm_size": "Standard_DS2_v2" - } - ], - "provisioners": [{ - "execute_command": "chmod +x {{ .Path }}; {{ .Vars }} sudo -E sh '{{ .Path }}'", - "inline": [ - "apt-get update", - "apt-get upgrade -y", - "/usr/sbin/waagent -force -deprovision+user && export HISTSIZE=0 && sync" - ], - "inline_shebang": "/bin/sh -x", - "type": "shell" - }] -} diff --git a/builder/azure/examples/marketplace_plan_info.json b/builder/azure/examples/marketplace_plan_info.json deleted file mode 100644 index 5cf58e280..000000000 --- a/builder/azure/examples/marketplace_plan_info.json +++ /dev/null @@ -1,47 +0,0 @@ -{ - "variables": { - "client_id": "{{env `ARM_CLIENT_ID`}}", - "client_secret": "{{env `ARM_CLIENT_SECRET`}}", - "subscription_id": "{{env `ARM_SUBSCRIPTION_ID`}}" - }, - "builders": [{ - "type": "azure-arm", - - "client_id": "{{user `client_id`}}", - "client_secret": "{{user `client_secret`}}", - "subscription_id": "{{user `subscription_id`}}", - - "managed_image_resource_group_name": "packertest", - "managed_image_name": "MyMarketplaceOSImage", - - "os_type": "Linux", - "image_publisher": "bitnami", - "image_offer": "rabbitmq", - "image_sku": "rabbitmq", - - "azure_tags": { - "dept": "engineering", - "task": "image deployment" - }, - - "plan_info": { - "plan_name": "rabbitmq", - "plan_product": "rabbitmq", - "plan_publisher": "bitnami" - }, - - "location": "West US", - "vm_size": "Standard_DS2_v2" - }], - "provisioners": [{ - "execute_command": "chmod +x {{ .Path }}; {{ .Vars }} sudo -E sh '{{ .Path }}'", - "inline": [ - "apt-get update", - "apt-get upgrade -y", - - "/usr/sbin/waagent -force -deprovision+user && export HISTSIZE=0 && sync" - ], - "inline_shebang": "/bin/sh -x", - "type": "shell" - }] -} diff --git a/builder/azure/examples/rhel.json b/builder/azure/examples/rhel.json deleted file mode 100644 index 895d4079b..000000000 --- a/builder/azure/examples/rhel.json +++ /dev/null @@ -1,45 +0,0 @@ -{ - "variables": { - "client_id": "{{env `ARM_CLIENT_ID`}}", - "client_secret": "{{env `ARM_CLIENT_SECRET`}}", - "subscription_id": "{{env `ARM_SUBSCRIPTION_ID`}}", - "tenant_id": "{{env `ARM_TENANT_ID`}}", - "ssh_user": "centos", - "ssh_pass": "{{env `ARM_SSH_PASS`}}" - }, - "builders": [{ - "type": "azure-arm", - - "client_id": "{{user `client_id`}}", - "client_secret": "{{user `client_secret`}}", - "subscription_id": "{{user `subscription_id`}}", - "tenant_id": "{{user `tenant_id`}}", - - "managed_image_resource_group_name": "packertest", - "managed_image_name": "MyRedHatOSImage", - - - "ssh_username": "{{user `ssh_user`}}", - "ssh_password": "{{user `ssh_pass`}}", - - "os_type": "Linux", - "image_publisher": "RedHat", - "image_offer": "RHEL", - "image_sku": "7.3", - "image_version": "latest", - "ssh_pty": "true", - - "location": "South Central US", - "vm_size": "Standard_DS2_v2" - }], - "provisioners": [{ - "execute_command": "echo '{{user `ssh_pass`}}' | {{ .Vars }} sudo -S -E sh '{{ .Path }}'", - "inline": [ - "yum update -y", - "/usr/sbin/waagent -force -deprovision+user && export HISTSIZE=0 && sync" - ], - "inline_shebang": "/bin/sh -x", - "type": "shell", - "skip_clean": true - }] -} diff --git a/builder/azure/examples/suse.json b/builder/azure/examples/suse.json deleted file mode 100644 index 4c6ff8e9e..000000000 --- a/builder/azure/examples/suse.json +++ /dev/null @@ -1,42 +0,0 @@ -{ - "variables": { - "client_id": "{{env `ARM_CLIENT_ID`}}", - "client_secret": "{{env `ARM_CLIENT_SECRET`}}", - "subscription_id": "{{env `ARM_SUBSCRIPTION_ID`}}", - "ssh_user": "packer", - "ssh_pass": "{{env `ARM_SSH_PASS`}}" - }, - "builders": [{ - "type": "azure-arm", - - "client_id": "{{user `client_id`}}", - "client_secret": "{{user `client_secret`}}", - "subscription_id": "{{user `subscription_id`}}", - - "managed_image_resource_group_name": "packertest", - "managed_image_name": "MySuseOSImage", - - "ssh_username": "{{user `ssh_user`}}", - "ssh_password": "{{user `ssh_pass`}}", - - "os_type": "Linux", - "image_publisher": "SUSE", - "image_offer": "SLES", - "image_sku": "12-SP3", - "ssh_pty": "true", - - "location": "South Central US", - "vm_size": "Standard_DS2_v2" - }], - "provisioners": [{ - "execute_command": "echo '{{user `ssh_pass`}}' | {{ .Vars }} sudo -S -E sh '{{ .Path }}'", - "inline": [ - "zypper update -y", - - "/usr/sbin/waagent -force -deprovision+user && export HISTSIZE=0 && sync" - ], - "inline_shebang": "/bin/sh -x", - "skip_clean": true, - "type": "shell" - }] -} diff --git a/builder/azure/examples/ubuntu-chroot.json b/builder/azure/examples/ubuntu-chroot.json deleted file mode 100644 index e7cd330f7..000000000 --- a/builder/azure/examples/ubuntu-chroot.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "variables": { - "client_id": "{{env `ARM_CLIENT_ID`}}", - "client_secret": "{{env `ARM_CLIENT_SECRET`}}", - "subscription_id": "{{env `ARM_SUBSCRIPTION_ID`}}", - "resource_group": "{{env `ARM_IMAGE_RESOURCEGROUP_ID`}}", - "gallery_name": "{{env `ARM_GALLERY_NAME`}}" - }, - "builders": [{ - "type": "azure-chroot", - - "client_id": "{{user `client_id`}}", - "client_secret": "{{user `client_secret`}}", - "subscription_id": "{{user `subscription_id`}}", - - "source": "Canonical:UbuntuServer:20.04-LTS:latest", - - "shared_image_destination": { - "resource_group": "{{user `resource_group`}}", - "gallery_name": "{{user `gallery_name`}}", - "image_name": "MyUbuntuOSImage", - "image_version": "1.0.0", - "exclude_from_latest": false, - "target_regions": [ - { - "name": "eastus", - "replicas": "1", - "storage_account_type": "standard_zrs" - } - ] - } - }], - "provisioners": [{ - "inline": [ - "apt update", - "apt upgrade -y" - ], - "inline_shebang": "/bin/sh -x", - "type": "shell" - }] -} diff --git a/builder/azure/examples/ubuntu.json b/builder/azure/examples/ubuntu.json deleted file mode 100644 index eefe62299..000000000 --- a/builder/azure/examples/ubuntu.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "variables": { - "client_id": "{{env `ARM_CLIENT_ID`}}", - "client_secret": "{{env `ARM_CLIENT_SECRET`}}", - "subscription_id": "{{env `ARM_SUBSCRIPTION_ID`}}" - }, - "builders": [{ - "type": "azure-arm", - - "client_id": "{{user `client_id`}}", - "client_secret": "{{user `client_secret`}}", - "subscription_id": "{{user `subscription_id`}}", - - "os_type": "Linux", - "image_publisher": "Canonical", - "image_offer": "UbuntuServer", - "image_sku": "16.04-LTS", - - "managed_image_resource_group_name": "packertest", - "managed_image_name": "MyUbuntuImage", - - "azure_tags": { - "dept": "engineering", - "task": "image deployment" - }, - - "location": "South Central US", - "vm_size": "Standard_DS2_v2" - }], - "provisioners": [{ - "execute_command": "chmod +x {{ .Path }}; {{ .Vars }} sudo -E sh '{{ .Path }}'", - "inline": [ - "apt-get update", - "apt-get upgrade -y", - - "/usr/sbin/waagent -force -deprovision+user && export HISTSIZE=0 && sync" - ], - "inline_shebang": "/bin/sh -x", - "type": "shell" - }] -} diff --git a/builder/azure/examples/ubuntu_managed_image_sig.json b/builder/azure/examples/ubuntu_managed_image_sig.json deleted file mode 100644 index 4789ad891..000000000 --- a/builder/azure/examples/ubuntu_managed_image_sig.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "variables": { - "client_id": "{{env `ARM_CLIENT_ID`}}", - "client_secret": "{{env `ARM_CLIENT_SECRET`}}", - "subscription_id": "{{env `ARM_SUBSCRIPTION_ID`}}", - "tenant_id": "{{env `ARM_TENANT_ID`}}" - }, - "builders": [{ - "type": "azure-arm", - - "client_id": "{{user `client_id`}}", - "client_secret": "{{user `client_secret`}}", - "tenant_id": "{{user `tenant_id`}}", - "subscription_id": "{{user `subscription_id`}}", - - "os_type": "Linux", - "image_publisher": "Canonical", - "image_offer": "UbuntuServer", - "image_sku": "16.04-LTS", - - "location": "West Central US", - "vm_size": "Standard_DS2_v2", - - "managed_image_resource_group_name": "PackerSigRGManagedImageRG", - "managed_image_name": "demo-image-sig-packer", - "shared_image_gallery_destination": { - "resource_group": "PackerSigPublishRG", - "gallery_name": "PackerSigGallery", - "image_name": "PackerSigImageDefinition", - "image_version": "1.0.0", - "replication_regions": ["South Central US"] - } - }], - "provisioners": [{ - "execute_command": "chmod +x {{ .Path }}; {{ .Vars }} sudo -E sh '{{ .Path }}'", - "inline": [ - "apt-get update", - "apt-get upgrade -y", - - "/usr/sbin/waagent -force -deprovision+user && export HISTSIZE=0 && sync" - ], - "inline_shebang": "/bin/sh -x", - "type": "shell" - }] -} - diff --git a/builder/azure/examples/ubuntu_quickstart.json b/builder/azure/examples/ubuntu_quickstart.json deleted file mode 100644 index e405d9eb9..000000000 --- a/builder/azure/examples/ubuntu_quickstart.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "variables": { - "resource_group": "{{env `ARM_RESOURCE_GROUP`}}", - "storage_account": "{{env `ARM_STORAGE_ACCOUNT`}}", - "subscription_id": "{{env `ARM_SUBSCRIPTION_ID`}}" - }, - "builders": [{ - "type": "azure-arm", - - "resource_group_name": "{{user `resource_group`}}", - "storage_account": "{{user `storage_account`}}", - "subscription_id": "{{user `subscription_id`}}", - - "capture_container_name": "images", - "capture_name_prefix": "packer", - - "os_type": "Linux", - "image_publisher": "Canonical", - "image_offer": "UbuntuServer", - "image_sku": "16.04-LTS", - - "location": "West US", - "vm_size": "Standard_DS2_v2" - }], - "provisioners": [{ - "execute_command": "chmod +x {{ .Path }}; {{ .Vars }} sudo -E sh '{{ .Path }}'", - "inline": [ - "apt-get update", - "apt-get upgrade -y", - "/usr/sbin/waagent -force -deprovision+user && export HISTSIZE=0 && sync" - ], - "inline_shebang": "/bin/sh -x", - "type": "shell" - }] -} diff --git a/builder/azure/examples/windows.json b/builder/azure/examples/windows.json deleted file mode 100644 index c1ee94d3d..000000000 --- a/builder/azure/examples/windows.json +++ /dev/null @@ -1,45 +0,0 @@ -{ - "variables": { - "client_id": "{{env `ARM_CLIENT_ID`}}", - "client_secret": "{{env `ARM_CLIENT_SECRET`}}", - "subscription_id": "{{env `ARM_SUBSCRIPTION_ID`}}" - }, - "builders": [{ - "type": "azure-arm", - - "client_id": "{{user `client_id`}}", - "client_secret": "{{user `client_secret`}}", - "subscription_id": "{{user `subscription_id`}}", - - "managed_image_resource_group_name": "packertest", - "managed_image_name": "MyWindowsOSImage", - - "os_type": "Windows", - "image_publisher": "MicrosoftWindowsServer", - "image_offer": "WindowsServer", - "image_sku": "2012-R2-Datacenter", - - "communicator": "winrm", - "winrm_use_ssl": "true", - "winrm_insecure": "true", - "winrm_timeout": "3m", - "winrm_username": "packer", - - "location": "South Central US", - "vm_size": "Standard_DS2_v2" - }], - "provisioners": [{ - "type": "powershell", - "inline": [ - " # NOTE: the following *3* lines are only needed if the you have installed the Guest Agent.", - " while ((Get-Service RdAgent).Status -ne 'Running') { Start-Sleep -s 5 }", - " while ((Get-Service WindowsAzureTelemetryService).Status -ne 'Running') { Start-Sleep -s 5 }", - " while ((Get-Service WindowsAzureGuestAgent).Status -ne 'Running') { Start-Sleep -s 5 }", - - "if( Test-Path $Env:SystemRoot\\windows\\system32\\Sysprep\\unattend.xml ){ rm $Env:SystemRoot\\windows\\system32\\Sysprep\\unattend.xml -Force}", - "& $env:SystemRoot\\System32\\Sysprep\\Sysprep.exe /oobe /generalize /quiet /quit /mode:vm", - "while($true) { $imageState = Get-ItemProperty HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup\\State | Select ImageState; Write-Output $imageState.ImageState; if($imageState.ImageState -ne 'IMAGE_STATE_GENERALIZE_RESEAL_TO_OOBE') { Start-Sleep -s 10 } else { break } }" - ] - }] -} - diff --git a/builder/azure/examples/windows_custom_image.json b/builder/azure/examples/windows_custom_image.json deleted file mode 100644 index a6e3d0057..000000000 --- a/builder/azure/examples/windows_custom_image.json +++ /dev/null @@ -1,49 +0,0 @@ -{ - "variables": { - "client_id": "{{env `ARM_CLIENT_ID`}}", - "client_secret": "{{env `ARM_CLIENT_SECRET`}}", - "resource_group": "{{env `ARM_RESOURCE_GROUP`}}", - "storage_account": "{{env `ARM_STORAGE_ACCOUNT`}}", - "subscription_id": "{{env `ARM_SUBSCRIPTION_ID`}}", - "object_id": "{{env `ARM_OBJECT_ID`}}" - }, - "builders": [ - { - "type": "azure-arm", - - "client_id": "{{user `client_id`}}", - "client_secret": "{{user `client_secret`}}", - "resource_group_name": "{{user `resource_group`}}", - "storage_account": "{{user `storage_account`}}", - "subscription_id": "{{user `subscription_id`}}", - "object_id": "{{user `object_id`}}", - - "capture_container_name": "images", - "capture_name_prefix": "packer", - - "os_type": "Windows", - "image_url": "https://my-storage-account.blob.core.windows.net/path/to/your/custom/image.vhd", - - "azure_tags": { - "dept": "engineering", - "task": "image deployment" - }, - - "location": "West US", - "vm_size": "Standard_DS2_v2" - } - ], - "provisioners": [{ - "type": "powershell", - "inline": [ - " # NOTE: the following *3* lines are only needed if the you have installed the Guest Agent.", - " while ((Get-Service RdAgent).Status -ne 'Running') { Start-Sleep -s 5 }", - " while ((Get-Service WindowsAzureTelemetryService).Status -ne 'Running') { Start-Sleep -s 5 }", - " while ((Get-Service WindowsAzureGuestAgent).Status -ne 'Running') { Start-Sleep -s 5 }", - - "if( Test-Path $Env:SystemRoot\\windows\\system32\\Sysprep\\unattend.xml ){ rm $Env:SystemRoot\\windows\\system32\\Sysprep\\unattend.xml -Force}", - "& $env:SystemRoot\\System32\\Sysprep\\Sysprep.exe /oobe /generalize /quiet /quit /mode:vm", - "while($true) { $imageState = Get-ItemProperty HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup\\State | Select ImageState; if($imageState.ImageState -ne 'IMAGE_STATE_GENERALIZE_RESEAL_TO_OOBE') { Write-Output $imageState.ImageState; Start-Sleep -s 10 } else { break } }" - ] - }] -} diff --git a/builder/azure/examples/windows_quickstart.json b/builder/azure/examples/windows_quickstart.json deleted file mode 100644 index 6d4fffb57..000000000 --- a/builder/azure/examples/windows_quickstart.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "variables": { - "subscription_id": "{{env `ARM_SUBSCRIPTION_ID`}}" - }, - "builders": [{ - "type": "azure-arm", - - "subscription_id": "{{user `subscription_id`}}", - - "managed_image_resource_group_name": "packertest", - "managed_image_name": "MyWindowsOSImage", - - "os_type": "Windows", - "image_publisher": "MicrosoftWindowsServer", - "image_offer": "WindowsServer", - "image_sku": "2012-R2-Datacenter", - - "communicator": "winrm", - "winrm_use_ssl": "true", - "winrm_insecure": "true", - "winrm_timeout": "3m", - "winrm_username": "packer", - - "location": "South Central US", - "vm_size": "Standard_DS2_v2" - }], - "provisioners": [{ - "type": "powershell", - "inline": [ - " # NOTE: the following *3* lines are only needed if the you have installed the Guest Agent.", - " while ((Get-Service RdAgent).Status -ne 'Running') { Start-Sleep -s 5 }", - " while ((Get-Service WindowsAzureTelemetryService).Status -ne 'Running') { Start-Sleep -s 5 }", - " while ((Get-Service WindowsAzureGuestAgent).Status -ne 'Running') { Start-Sleep -s 5 }", - - "if( Test-Path $Env:SystemRoot\\windows\\system32\\Sysprep\\unattend.xml ){ rm $Env:SystemRoot\\windows\\system32\\Sysprep\\unattend.xml -Force}", - "& $env:SystemRoot\\System32\\Sysprep\\Sysprep.exe /oobe /generalize /quiet /quit /mode:vm", - "while($true) { $imageState = Get-ItemProperty HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup\\State | Select ImageState; if($imageState.ImageState -ne 'IMAGE_STATE_GENERALIZE_RESEAL_TO_OOBE') { Write-Output $imageState.ImageState; Start-Sleep -s 10 } else { break } }" - ] - }] -} - diff --git a/builder/azure/pkcs12/LICENSE b/builder/azure/pkcs12/LICENSE deleted file mode 100644 index 6a66aea5e..000000000 --- a/builder/azure/pkcs12/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -Copyright (c) 2009 The Go Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/builder/azure/pkcs12/bmp-string.go b/builder/azure/pkcs12/bmp-string.go deleted file mode 100644 index 284d2a68f..000000000 --- a/builder/azure/pkcs12/bmp-string.go +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright 2015 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package pkcs12 - -import ( - "errors" - "unicode/utf16" -) - -// bmpString returns s encoded in UCS-2 with a zero terminator. -func bmpString(s string) ([]byte, error) { - // References: - // https://tools.ietf.org/html/rfc7292#appendix-B.1 - // http://en.wikipedia.org/wiki/Plane_(Unicode)#Basic_Multilingual_Plane - // - non-BMP characters are encoded in UTF 16 by using a surrogate pair of 16-bit codes - // EncodeRune returns 0xfffd if the rune does not need special encoding - // - the above RFC provides the info that BMPStrings are NULL terminated. - - ret := make([]byte, 0, 2*len(s)+2) - - for _, r := range s { - if t, _ := utf16.EncodeRune(r); t != 0xfffd { - return nil, errors.New("pkcs12: string contains characters that cannot be encoded in UCS-2") - } - ret = append(ret, byte(r/256), byte(r%256)) - } - - return append(ret, 0, 0), nil -} - -func decodeBMPString(bmpString []byte) (string, error) { - if len(bmpString)%2 != 0 { - return "", errors.New("pkcs12: odd-length BMP string") - } - - // strip terminator if present - if l := len(bmpString); l >= 2 && bmpString[l-1] == 0 && bmpString[l-2] == 0 { - bmpString = bmpString[:l-2] - } - - s := make([]uint16, 0, len(bmpString)/2) - for len(bmpString) > 0 { - s = append(s, uint16(bmpString[0])<<8+uint16(bmpString[1])) - bmpString = bmpString[2:] - } - - return string(utf16.Decode(s)), nil -} diff --git a/builder/azure/pkcs12/bmp-string_test.go b/builder/azure/pkcs12/bmp-string_test.go deleted file mode 100644 index 711528b81..000000000 --- a/builder/azure/pkcs12/bmp-string_test.go +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright 2015 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package pkcs12 - -import ( - "bytes" - "encoding/hex" - "testing" -) - -var bmpStringTests = []struct { - in string - expectedHex string - shouldFail bool -}{ - {"", "0000", false}, - // Example from https://tools.ietf.org/html/rfc7292#appendix-B. - {"Beavis", "0042006500610076006900730000", false}, - // Some characters from the "Letterlike Symbols Unicode block". - {"\u2115 - Double-struck N", "21150020002d00200044006f00750062006c0065002d00730074007200750063006b0020004e0000", false}, - // any character outside the BMP should trigger an error. - {"\U0001f000 East wind (Mahjong)", "", true}, -} - -func TestBMPStringDecode(t *testing.T) { - if _, err := decodeBMPString([]byte("a")); err == nil { - t.Fatalf("expected decode to fail, but it succeeded") - } -} - -func TestBMPString(t *testing.T) { - for i, test := range bmpStringTests { - expected, err := hex.DecodeString(test.expectedHex) - if err != nil { - t.Fatalf("#%d: failed to decode expectation", i) - } - - out, err := bmpString(test.in) - if err == nil && test.shouldFail { - t.Errorf("#%d: expected to fail, but produced %x", i, out) - continue - } - - if err != nil && !test.shouldFail { - t.Errorf("#%d: failed unexpectedly: %s", i, err) - continue - } - - if !test.shouldFail { - if !bytes.Equal(out, expected) { - t.Errorf("#%d: expected %s, got %x", i, test.expectedHex, out) - continue - } - - roundTrip, err := decodeBMPString(out) - if err != nil { - t.Errorf("#%d: decoding output gave an error: %s", i, err) - continue - } - - if roundTrip != test.in { - t.Errorf("#%d: decoding output resulted in %q, but it should have been %q", i, roundTrip, test.in) - continue - } - } - } -} diff --git a/builder/azure/pkcs12/crypto.go b/builder/azure/pkcs12/crypto.go deleted file mode 100644 index 6c81eb774..000000000 --- a/builder/azure/pkcs12/crypto.go +++ /dev/null @@ -1,167 +0,0 @@ -// Copyright 2015 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package pkcs12 - -import ( - "bytes" - "crypto/cipher" - "crypto/des" - "crypto/rand" - "crypto/x509/pkix" - "encoding/asn1" - "errors" - "io" - - "github.com/hashicorp/packer/builder/azure/pkcs12/rc2" -) - -const ( - pbeIterationCount = 2048 - pbeSaltSizeBytes = 8 -) - -var ( - oidPBEWithSHAAnd3KeyTripleDESCBC = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 12, 1, 3}) - oidPBEWithSHAAnd40BitRC2CBC = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 12, 1, 6}) -) - -// pbeCipher is an abstraction of a PKCS#12 cipher. -type pbeCipher interface { - // create returns a cipher.Block given a key. - create(key []byte) (cipher.Block, error) - // deriveKey returns a key derived from the given password and salt. - deriveKey(salt, password []byte, iterations int) []byte - // deriveKey returns an IV derived from the given password and salt. - deriveIV(salt, password []byte, iterations int) []byte -} - -type shaWithTripleDESCBC struct{} - -func (shaWithTripleDESCBC) create(key []byte) (cipher.Block, error) { - return des.NewTripleDESCipher(key) -} - -func (shaWithTripleDESCBC) deriveKey(salt, password []byte, iterations int) []byte { - return pbkdf(sha1Sum, 20, 64, salt, password, iterations, 1, 24) -} - -func (shaWithTripleDESCBC) deriveIV(salt, password []byte, iterations int) []byte { - return pbkdf(sha1Sum, 20, 64, salt, password, iterations, 2, 8) -} - -type shaWith40BitRC2CBC struct{} - -func (shaWith40BitRC2CBC) create(key []byte) (cipher.Block, error) { - return rc2.New(key, len(key)*8) -} - -func (shaWith40BitRC2CBC) deriveKey(salt, password []byte, iterations int) []byte { - return pbkdf(sha1Sum, 20, 64, salt, password, iterations, 1, 5) -} - -func (shaWith40BitRC2CBC) deriveIV(salt, password []byte, iterations int) []byte { - return pbkdf(sha1Sum, 20, 64, salt, password, iterations, 2, 8) -} - -type pbeParams struct { - Salt []byte - Iterations int -} - -func pbDecrypterFor(algorithm pkix.AlgorithmIdentifier, password []byte) (cipher.BlockMode, int, error) { - var cipherType pbeCipher - - switch { - case algorithm.Algorithm.Equal(oidPBEWithSHAAnd3KeyTripleDESCBC): - cipherType = shaWithTripleDESCBC{} - case algorithm.Algorithm.Equal(oidPBEWithSHAAnd40BitRC2CBC): - cipherType = shaWith40BitRC2CBC{} - default: - return nil, 0, NotImplementedError("algorithm " + algorithm.Algorithm.String() + " is not supported") - } - - var params pbeParams - if err := unmarshal(algorithm.Parameters.FullBytes, ¶ms); err != nil { - return nil, 0, err - } - - key := cipherType.deriveKey(params.Salt, password, params.Iterations) - iv := cipherType.deriveIV(params.Salt, password, params.Iterations) - - block, err := cipherType.create(key) - if err != nil { - return nil, 0, err - } - - return cipher.NewCBCDecrypter(block, iv), block.BlockSize(), nil -} - -func pbDecrypt(info decryptable, password []byte) (decrypted []byte, err error) { - cbc, blockSize, err := pbDecrypterFor(info.Algorithm(), password) - if err != nil { - return nil, err - } - - encrypted := info.Data() - if len(encrypted) == 0 { - return nil, errors.New("pkcs12: empty encrypted data") - } - if len(encrypted)%blockSize != 0 { - return nil, errors.New("pkcs12: input is not a multiple of the block size") - } - decrypted = make([]byte, len(encrypted)) - cbc.CryptBlocks(decrypted, encrypted) - - psLen := int(decrypted[len(decrypted)-1]) - if psLen == 0 || psLen > blockSize { - return nil, ErrDecryption - } - - if len(decrypted) < psLen { - return nil, ErrDecryption - } - ps := decrypted[len(decrypted)-psLen:] - decrypted = decrypted[:len(decrypted)-psLen] - if bytes.Compare(ps, bytes.Repeat([]byte{byte(psLen)}, psLen)) != 0 { - return nil, ErrDecryption - } - - return -} - -func pad(src []byte, blockSize int) []byte { - paddingLength := blockSize - len(src)%blockSize - paddingText := bytes.Repeat([]byte{byte(paddingLength)}, paddingLength) - return append(src, paddingText...) -} - -func pbEncrypt(plainText, salt, password []byte, iterations int) (cipherText []byte, err error) { - if _, err := io.ReadFull(rand.Reader, salt); err != nil { - return nil, errors.New("pkcs12: failed to create a random salt value: " + err.Error()) - } - - cipherType := shaWithTripleDESCBC{} - key := cipherType.deriveKey(salt, password, iterations) - iv := cipherType.deriveIV(salt, password, iterations) - - block, err := cipherType.create(key) - if err != nil { - return nil, errors.New("pkcs12: failed to create a block cipher: " + err.Error()) - } - - paddedPlainText := pad(plainText, block.BlockSize()) - - encrypter := cipher.NewCBCEncrypter(block, iv) - cipherText = make([]byte, len(paddedPlainText)) - encrypter.CryptBlocks(cipherText, paddedPlainText) - - return cipherText, nil -} - -// decryptable abstracts a object that contains ciphertext. -type decryptable interface { - Algorithm() pkix.AlgorithmIdentifier - Data() []byte -} diff --git a/builder/azure/pkcs12/crypto_test.go b/builder/azure/pkcs12/crypto_test.go deleted file mode 100644 index efcd90820..000000000 --- a/builder/azure/pkcs12/crypto_test.go +++ /dev/null @@ -1,168 +0,0 @@ -// Copyright 2015 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package pkcs12 - -import ( - "bytes" - "crypto/rand" - "crypto/x509/pkix" - "encoding/asn1" - "io" - "testing" -) - -var sha1WithTripleDES = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 12, 1, 3}) - -func TestPbDecrypterFor(t *testing.T) { - params, _ := asn1.Marshal(pbeParams{ - Salt: []byte{1, 2, 3, 4, 5, 6, 7, 8}, - Iterations: 2048, - }) - alg := pkix.AlgorithmIdentifier{ - Algorithm: asn1.ObjectIdentifier([]int{1, 2, 3}), - Parameters: asn1.RawValue{ - FullBytes: params, - }, - } - - pass, _ := bmpString("Sesame open") - - _, _, err := pbDecrypterFor(alg, pass) - if _, ok := err.(NotImplementedError); !ok { - t.Errorf("expected not implemented error, got: %T %s", err, err) - } - - alg.Algorithm = sha1WithTripleDES - cbc, blockSize, err := pbDecrypterFor(alg, pass) - if err != nil { - t.Errorf("unexpected error from pbDecrypterFor %v", err) - } - if blockSize != 8 { - t.Errorf("unexpected block size %d, wanted 8", blockSize) - } - - plaintext := []byte{1, 2, 3, 4, 5, 6, 7, 8} - expectedCiphertext := []byte{185, 73, 135, 249, 137, 1, 122, 247} - ciphertext := make([]byte, len(plaintext)) - cbc.CryptBlocks(ciphertext, plaintext) - - if bytes.Compare(ciphertext, expectedCiphertext) != 0 { - t.Errorf("bad ciphertext, got %x but wanted %x", ciphertext, expectedCiphertext) - } -} - -var pbDecryptTests = []struct { - in []byte - expected []byte - expectedError error -}{ - { - []byte("\x33\x73\xf3\x9f\xda\x49\xae\xfc\xa0\x9a\xdf\x5a\x58\xa0\xea\x46"), // 7 padding bytes - []byte("A secret!"), - nil, - }, - { - []byte("\x33\x73\xf3\x9f\xda\x49\xae\xfc\x96\x24\x2f\x71\x7e\x32\x3f\xe7"), // 8 padding bytes - []byte("A secret"), - nil, - }, - { - []byte("\x35\x0c\xc0\x8d\xab\xa9\x5d\x30\x7f\x9a\xec\x6a\xd8\x9b\x9c\xd9"), // 9 padding bytes, incorrect - nil, - ErrDecryption, - }, - { - []byte("\xb2\xf9\x6e\x06\x60\xae\x20\xcf\x08\xa0\x7b\xd9\x6b\x20\xef\x41"), // incorrect padding bytes: [ ... 0x04 0x02 ] - nil, - ErrDecryption, - }, -} - -func TestPbDecrypt(t *testing.T) { - salt := []byte("\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8") - - for i, test := range pbDecryptTests { - decryptable := makeTestDecryptable(test.in, salt) - password, _ := bmpString("sesame") - - plaintext, err := pbDecrypt(decryptable, password) - if err != test.expectedError { - t.Errorf("#%d: got error %q, but wanted %q", i, err, test.expectedError) - continue - } - - if !bytes.Equal(plaintext, test.expected) { - t.Errorf("#%d: got %x, but wanted %x", i, plaintext, test.expected) - } - } -} - -func TestRoundTripPkc12EncryptDecrypt(t *testing.T) { - salt := []byte{0xfe, 0xee, 0xfa, 0xce} - password := salt - - // Sweep the possible padding lengths - for i := 0; i < 9; i++ { - bs := make([]byte, i) - _, err := io.ReadFull(rand.Reader, bs) - if err != nil { - t.Fatalf("failed to read: %s", err) - } - - cipherText, err := pbEncrypt(bs, salt, password, 4096) - if err != nil { - t.Fatalf("failed to encrypt: %s\n", err) - } - - if len(cipherText)%8 != 0 { - t.Fatalf("plain text was not padded as expected") - } - - decryptable := makeTestDecryptable(cipherText, salt) - plainText, err := pbDecrypt(decryptable, password) - if err != nil { - t.Fatalf("failed to decrypt: %s\n", err) - } - - if !bytes.Equal(bs, plainText) { - t.Fatalf("got %x, but wanted %x", bs, plainText) - } - } -} - -func makeTestDecryptable(bytes, salt []byte) testDecryptable { - decryptable := testDecryptable{ - data: bytes, - algorithm: pkix.AlgorithmIdentifier{ - Algorithm: sha1WithTripleDES, - Parameters: pbeParams{ - Salt: salt, - Iterations: 4096, - }.RawASN1(), - }, - } - - return decryptable -} - -type testDecryptable struct { - data []byte - algorithm pkix.AlgorithmIdentifier -} - -func (d testDecryptable) Algorithm() pkix.AlgorithmIdentifier { return d.algorithm } -func (d testDecryptable) Data() []byte { return d.data } - -func (params pbeParams) RawASN1() (raw asn1.RawValue) { - asn1Bytes, err := asn1.Marshal(params) - if err != nil { - panic(err) - } - _, err = asn1.Unmarshal(asn1Bytes, &raw) - if err != nil { - panic(err) - } - return -} diff --git a/builder/azure/pkcs12/errors.go b/builder/azure/pkcs12/errors.go deleted file mode 100644 index 36ad6e957..000000000 --- a/builder/azure/pkcs12/errors.go +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2015 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package pkcs12 - -import "errors" - -var ( - // ErrDecryption represents a failure to decrypt the input. - ErrDecryption = errors.New("pkcs12: decryption error, incorrect padding") - - // ErrIncorrectPassword is returned when an incorrect password is detected. - // Usually, P12/PFX data is signed to be able to verify the password. - ErrIncorrectPassword = errors.New("pkcs12: decryption password incorrect") -) - -// NotImplementedError indicates that the input is not currently supported. -type NotImplementedError string -type EncodeError string - -func (e NotImplementedError) Error() string { - return "pkcs12: " + string(e) -} - -func (e EncodeError) Error() string { - return "pkcs12: encode error: " + string(e) -} diff --git a/builder/azure/pkcs12/mac.go b/builder/azure/pkcs12/mac.go deleted file mode 100644 index 76ad0cdc5..000000000 --- a/builder/azure/pkcs12/mac.go +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright 2015 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package pkcs12 - -import ( - "crypto/hmac" - "crypto/sha1" - "crypto/x509/pkix" - "encoding/asn1" -) - -type macData struct { - Mac digestInfo - MacSalt []byte - Iterations int `asn1:"optional,default:1"` -} - -// from PKCS#7: -type digestInfo struct { - Algorithm pkix.AlgorithmIdentifier - Digest []byte -} - -var ( - oidSHA1 = asn1.ObjectIdentifier([]int{1, 3, 14, 3, 2, 26}) -) - -func verifyMac(macData *macData, message, password []byte) error { - if !macData.Mac.Algorithm.Algorithm.Equal(oidSHA1) { - return NotImplementedError("unknown digest algorithm: " + macData.Mac.Algorithm.Algorithm.String()) - } - - expectedMAC := computeMac(message, macData.Iterations, macData.MacSalt, password) - - if !hmac.Equal(macData.Mac.Digest, expectedMAC) { - return ErrIncorrectPassword - } - return nil -} - -func computeMac(message []byte, iterations int, salt, password []byte) []byte { - key := pbkdf(sha1Sum, 20, 64, salt, password, iterations, 3, 20) - - mac := hmac.New(sha1.New, key) - mac.Write(message) - - return mac.Sum(nil) -} diff --git a/builder/azure/pkcs12/mac_test.go b/builder/azure/pkcs12/mac_test.go deleted file mode 100644 index 1ed4ff21e..000000000 --- a/builder/azure/pkcs12/mac_test.go +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2015 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package pkcs12 - -import ( - "encoding/asn1" - "testing" -) - -func TestVerifyMac(t *testing.T) { - td := macData{ - Mac: digestInfo{ - Digest: []byte{0x18, 0x20, 0x3d, 0xff, 0x1e, 0x16, 0xf4, 0x92, 0xf2, 0xaf, 0xc8, 0x91, 0xa9, 0xba, 0xd6, 0xca, 0x9d, 0xee, 0x51, 0x93}, - }, - MacSalt: []byte{1, 2, 3, 4, 5, 6, 7, 8}, - Iterations: 2048, - } - - message := []byte{11, 12, 13, 14, 15} - password, _ := bmpString("") - - td.Mac.Algorithm.Algorithm = asn1.ObjectIdentifier([]int{1, 2, 3}) - err := verifyMac(&td, message, password) - if _, ok := err.(NotImplementedError); !ok { - t.Errorf("err: %v", err) - } - - td.Mac.Algorithm.Algorithm = asn1.ObjectIdentifier([]int{1, 3, 14, 3, 2, 26}) - err = verifyMac(&td, message, password) - if err != ErrIncorrectPassword { - t.Errorf("Expected incorrect password, got err: %v", err) - } - - password, _ = bmpString("Sesame open") - err = verifyMac(&td, message, password) - if err != nil { - t.Errorf("err: %v", err) - } - -} diff --git a/builder/azure/pkcs12/pbkdf.go b/builder/azure/pkcs12/pbkdf.go deleted file mode 100644 index 5c419d41e..000000000 --- a/builder/azure/pkcs12/pbkdf.go +++ /dev/null @@ -1,170 +0,0 @@ -// Copyright 2015 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package pkcs12 - -import ( - "bytes" - "crypto/sha1" - "math/big" -) - -var ( - one = big.NewInt(1) -) - -// sha1Sum returns the SHA-1 hash of in. -func sha1Sum(in []byte) []byte { - sum := sha1.Sum(in) - return sum[:] -} - -// fillWithRepeats returns v*ceiling(len(pattern) / v) bytes consisting of -// repeats of pattern. -func fillWithRepeats(pattern []byte, v int) []byte { - if len(pattern) == 0 { - return nil - } - outputLen := v * ((len(pattern) + v - 1) / v) - return bytes.Repeat(pattern, (outputLen+len(pattern)-1)/len(pattern))[:outputLen] -} - -func pbkdf(hash func([]byte) []byte, u, v int, salt, password []byte, r int, ID byte, size int) (key []byte) { - // implementation of https://tools.ietf.org/html/rfc7292#appendix-B.2 , RFC text verbatim in comments - - // Let H be a hash function built around a compression function f: - - // Z_2^u x Z_2^v -> Z_2^u - - // (that is, H has a chaining variable and output of length u bits, and - // the message input to the compression function of H is v bits). The - // values for u and v are as follows: - - // HASH FUNCTION VALUE u VALUE v - // MD2, MD5 128 512 - // SHA-1 160 512 - // SHA-224 224 512 - // SHA-256 256 512 - // SHA-384 384 1024 - // SHA-512 512 1024 - // SHA-512/224 224 1024 - // SHA-512/256 256 1024 - - // Furthermore, let r be the iteration count. - - // We assume here that u and v are both multiples of 8, as are the - // lengths of the password and salt strings (which we denote by p and s, - // respectively) and the number n of pseudorandom bits required. In - // addition, u and v are of course non-zero. - - // For information on security considerations for MD5 [19], see [25] and - // [1], and on those for MD2, see [18]. - - // The following procedure can be used to produce pseudorandom bits for - // a particular "purpose" that is identified by a byte called "ID". - // This standard specifies 3 different values for the ID byte: - - // 1. If ID=1, then the pseudorandom bits being produced are to be used - // as key material for performing encryption or decryption. - - // 2. If ID=2, then the pseudorandom bits being produced are to be used - // as an IV (Initial Value) for encryption or decryption. - - // 3. If ID=3, then the pseudorandom bits being produced are to be used - // as an integrity key for MACing. - - // 1. Construct a string, D (the "diversifier"), by concatenating v/8 - // copies of ID. - var D []byte - for i := 0; i < v; i++ { - D = append(D, ID) - } - - // 2. Concatenate copies of the salt together to create a string S of - // length v(ceiling(s/v)) bits (the final copy of the salt may be - // truncated to create S). Note that if the salt is the empty - // string, then so is S. - - S := fillWithRepeats(salt, v) - - // 3. Concatenate copies of the password together to create a string P - // of length v(ceiling(p/v)) bits (the final copy of the password - // may be truncated to create P). Note that if the password is the - // empty string, then so is P. - - P := fillWithRepeats(password, v) - - // 4. Set I=S||P to be the concatenation of S and P. - I := append(S, P...) - - // 5. Set c=ceiling(n/u). - c := (size + u - 1) / u - - // 6. For i=1, 2, ..., c, do the following: - A := make([]byte, c*20) - var IjBuf []byte - for i := 0; i < c; i++ { - // A. Set A2=H^r(D||I). (i.e., the r-th hash of D||1, - // H(H(H(... H(D||I)))) - Ai := hash(append(D, I...)) - for j := 1; j < r; j++ { - Ai = hash(Ai) - } - copy(A[i*20:], Ai[:]) - - if i < c-1 { // skip on last iteration - // B. Concatenate copies of Ai to create a string B of length v - // bits (the final copy of Ai may be truncated to create B). - var B []byte - for len(B) < v { - B = append(B, Ai[:]...) - } - B = B[:v] - - // C. Treating I as a concatenation I_0, I_1, ..., I_(k-1) of v-bit - // blocks, where k=ceiling(s/v)+ceiling(p/v), modify I by - // setting I_j=(I_j+B+1) mod 2^v for each j. - { - Bbi := new(big.Int).SetBytes(B) - Ij := new(big.Int) - - for j := 0; j < len(I)/v; j++ { - Ij.SetBytes(I[j*v : (j+1)*v]) - Ij.Add(Ij, Bbi) - Ij.Add(Ij, one) - Ijb := Ij.Bytes() - // We expect Ijb to be exactly v bytes, - // if it is longer or shorter we must - // adjust it accordingly. - if len(Ijb) > v { - Ijb = Ijb[len(Ijb)-v:] - } - if len(Ijb) < v { - if IjBuf == nil { - IjBuf = make([]byte, v) - } - bytesShort := v - len(Ijb) - for i := 0; i < bytesShort; i++ { - IjBuf[i] = 0 - } - copy(IjBuf[bytesShort:], Ijb) - Ijb = IjBuf - } - copy(I[j*v:(j+1)*v], Ijb) - } - } - } - } - // 7. Concatenate A_1, A_2, ..., A_c together to form a pseudorandom - // bit string, A. - - // 8. Use the first n bits of A as the output of this entire process. - return A[:size] - - // If the above process is being used to generate a DES key, the process - // should be used to create 64 random bits, and the key's parity bits - // should be set after the 64 bits have been produced. Similar concerns - // hold for 2-key and 3-key triple-DES keys, for CDMF keys, and for any - // similar keys with parity bits "built into them". -} diff --git a/builder/azure/pkcs12/pbkdf_test.go b/builder/azure/pkcs12/pbkdf_test.go deleted file mode 100644 index 262037d7e..000000000 --- a/builder/azure/pkcs12/pbkdf_test.go +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2015 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package pkcs12 - -import ( - "bytes" - "testing" -) - -func TestThatPBKDFWorksCorrectlyForLongKeys(t *testing.T) { - cipherInfo := shaWithTripleDESCBC{} - - salt := []byte("\xff\xff\xff\xff\xff\xff\xff\xff") - password, _ := bmpString("sesame") - key := cipherInfo.deriveKey(salt, password, 2048) - - if expected := []byte("\x7c\xd9\xfd\x3e\x2b\x3b\xe7\x69\x1a\x44\xe3\xbe\xf0\xf9\xea\x0f\xb9\xb8\x97\xd4\xe3\x25\xd9\xd1"); bytes.Compare(key, expected) != 0 { - t.Fatalf("expected key '%x', but found '%x'", expected, key) - } -} - -func TestThatPBKDFHandlesLeadingZeros(t *testing.T) { - // This test triggers a case where I_j (in step 6C) ends up with leading zero - // byte, meaning that len(Ijb) < v (leading zeros get stripped by big.Int). - // This was previously causing bug whereby certain inputs would break the - // derivation and produce the wrong output. - key := pbkdf(sha1Sum, 20, 64, []byte("\xf3\x7e\x05\xb5\x18\x32\x4b\x4b"), []byte("\x00\x00"), 2048, 1, 24) - expected := []byte("\x00\xf7\x59\xff\x47\xd1\x4d\xd0\x36\x65\xd5\x94\x3c\xb3\xc4\xa3\x9a\x25\x55\xc0\x2a\xed\x66\xe1") - if bytes.Compare(key, expected) != 0 { - t.Fatalf("expected key '%x', but found '%x'", expected, key) - } -} diff --git a/builder/azure/pkcs12/pkcs12.go b/builder/azure/pkcs12/pkcs12.go deleted file mode 100644 index c5ce90f0d..000000000 --- a/builder/azure/pkcs12/pkcs12.go +++ /dev/null @@ -1,530 +0,0 @@ -// Copyright 2015 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package pkcs12 implements some of PKCS#12. -// -// This implementation is distilled from https://tools.ietf.org/html/rfc7292 -// and referenced documents. It is intended for decoding P12/PFX-stored -// certificates and keys for use with the crypto/tls package. -package pkcs12 - -import ( - "crypto/ecdsa" - "crypto/rand" - "crypto/rsa" - "crypto/x509" - "crypto/x509/pkix" - "encoding/asn1" - "encoding/hex" - "encoding/pem" - "errors" - "io" -) - -var ( - oidDataContentType = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 7, 1}) - oidEncryptedDataContentType = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 7, 6}) - - oidFriendlyName = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 9, 20}) - oidLocalKeyID = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 9, 21}) - oidMicrosoftCSPName = asn1.ObjectIdentifier([]int{1, 3, 6, 1, 4, 1, 311, 17, 1}) - - localKeyId = []byte{0x01, 0x00, 0x00, 0x00} -) - -type pfxPdu struct { - Version int - AuthSafe contentInfo - MacData macData `asn1:"optional"` -} - -type contentInfo struct { - ContentType asn1.ObjectIdentifier - Content asn1.RawValue `asn1:"tag:0,explicit,optional"` -} - -type encryptedData struct { - Version int - EncryptedContentInfo encryptedContentInfo -} - -type encryptedContentInfo struct { - ContentType asn1.ObjectIdentifier - ContentEncryptionAlgorithm pkix.AlgorithmIdentifier - EncryptedContent []byte `asn1:"tag:0,optional"` -} - -func (i encryptedContentInfo) Algorithm() pkix.AlgorithmIdentifier { - return i.ContentEncryptionAlgorithm -} - -func (i encryptedContentInfo) Data() []byte { return i.EncryptedContent } - -type safeBag struct { - Id asn1.ObjectIdentifier - Value asn1.RawValue `asn1:"tag:0,explicit"` - Attributes []pkcs12Attribute `asn1:"set,optional"` -} - -type pkcs12Attribute struct { - Id asn1.ObjectIdentifier - Value asn1.RawValue `asn1:"set"` -} - -type encryptedPrivateKeyInfo struct { - AlgorithmIdentifier pkix.AlgorithmIdentifier - EncryptedData []byte -} - -func (i encryptedPrivateKeyInfo) Algorithm() pkix.AlgorithmIdentifier { - return i.AlgorithmIdentifier -} - -func (i encryptedPrivateKeyInfo) Data() []byte { - return i.EncryptedData -} - -// PEM block types -const ( - certificateType = "CERTIFICATE" - privateKeyType = "PRIVATE KEY" -) - -// unmarshal calls asn1.Unmarshal, but also returns an error if there is any -// trailing data after unmarshalling. -func unmarshal(in []byte, out interface{}) error { - trailing, err := asn1.Unmarshal(in, out) - if err != nil { - return err - } - if len(trailing) != 0 { - return errors.New("pkcs12: trailing data found") - } - return nil -} - -// ConvertToPEM converts all "safe bags" contained in pfxData to PEM blocks. -func ToPEM(pfxData []byte, password string) ([]*pem.Block, error) { - encodedPassword, err := bmpString(password) - if err != nil { - return nil, ErrIncorrectPassword - } - - bags, encodedPassword, err := getSafeContents(pfxData, encodedPassword) - - blocks := make([]*pem.Block, 0, len(bags)) - for _, bag := range bags { - block, err := convertBag(&bag, encodedPassword) - if err != nil { - return nil, err - } - blocks = append(blocks, block) - } - - return blocks, nil -} - -func convertBag(bag *safeBag, password []byte) (*pem.Block, error) { - block := &pem.Block{ - Headers: make(map[string]string), - } - - for _, attribute := range bag.Attributes { - k, v, err := convertAttribute(&attribute) - if err != nil { - return nil, err - } - block.Headers[k] = v - } - - switch { - case bag.Id.Equal(oidCertBag): - block.Type = certificateType - certsData, err := decodeCertBag(bag.Value.Bytes) - if err != nil { - return nil, err - } - block.Bytes = certsData - case bag.Id.Equal(oidPKCS8ShroudedKeyBag): - block.Type = privateKeyType - - key, err := decodePkcs8ShroudedKeyBag(bag.Value.Bytes, password) - if err != nil { - return nil, err - } - - switch key := key.(type) { - case *rsa.PrivateKey: - block.Bytes = x509.MarshalPKCS1PrivateKey(key) - case *ecdsa.PrivateKey: - block.Bytes, err = x509.MarshalECPrivateKey(key) - if err != nil { - return nil, err - } - default: - return nil, errors.New("found unknown private key type in PKCS#8 wrapping") - } - default: - return nil, errors.New("don't know how to convert a safe bag of type " + bag.Id.String()) - } - return block, nil -} - -func convertAttribute(attribute *pkcs12Attribute) (key, value string, err error) { - isString := false - - switch { - case attribute.Id.Equal(oidFriendlyName): - key = "friendlyName" - isString = true - case attribute.Id.Equal(oidLocalKeyID): - key = "localKeyId" - case attribute.Id.Equal(oidMicrosoftCSPName): - // This key is chosen to match OpenSSL. - key = "Microsoft CSP Name" - isString = true - default: - return "", "", errors.New("pkcs12: unknown attribute with OID " + attribute.Id.String()) - } - - if isString { - if err := unmarshal(attribute.Value.Bytes, &attribute.Value); err != nil { - return "", "", err - } - if value, err = decodeBMPString(attribute.Value.Bytes); err != nil { - return "", "", err - } - } else { - var id []byte - if err := unmarshal(attribute.Value.Bytes, &id); err != nil { - return "", "", err - } - value = hex.EncodeToString(id) - } - - return key, value, nil -} - -// Decode extracts a certificate and private key from pfxData. This function -// assumes that there is only one certificate and only one private key in the -// pfxData. -func Decode(pfxData []byte, password string) (privateKey interface{}, certificate *x509.Certificate, err error) { - encodedPassword, err := bmpString(password) - if err != nil { - return nil, nil, err - } - - bags, encodedPassword, err := getSafeContents(pfxData, encodedPassword) - if err != nil { - return nil, nil, err - } - - if len(bags) != 2 { - err = errors.New("pkcs12: expected exactly two safe bags in the PFX PDU") - return - } - - for _, bag := range bags { - switch { - case bag.Id.Equal(oidCertBag): - if certificate != nil { - err = errors.New("pkcs12: expected exactly one certificate bag") - } - - certsData, err := decodeCertBag(bag.Value.Bytes) - if err != nil { - return nil, nil, err - } - certs, err := x509.ParseCertificates(certsData) - if err != nil { - return nil, nil, err - } - if len(certs) != 1 { - err = errors.New("pkcs12: expected exactly one certificate in the certBag") - return nil, nil, err - } - certificate = certs[0] - - case bag.Id.Equal(oidPKCS8ShroudedKeyBag): - if privateKey != nil { - err = errors.New("pkcs12: expected exactly one key bag") - } - if privateKey, err = decodePkcs8ShroudedKeyBag(bag.Value.Bytes, encodedPassword); err != nil { - return nil, nil, err - } - } - } - - if certificate == nil { - return nil, nil, errors.New("pkcs12: certificate missing") - } - if privateKey == nil { - return nil, nil, errors.New("pkcs12: private key missing") - } - - return -} - -func getLocalKeyId(id []byte) (attribute pkcs12Attribute, err error) { - octetString := asn1.RawValue{Tag: 4, Class: 0, IsCompound: false, Bytes: id} - bytes, err := asn1.Marshal(octetString) - if err != nil { - return - } - - attribute = pkcs12Attribute{ - Id: oidLocalKeyID, - Value: asn1.RawValue{Tag: 17, Class: 0, IsCompound: true, Bytes: bytes}, - } - - return attribute, nil -} - -func convertToRawVal(val interface{}) (raw asn1.RawValue, err error) { - bytes, err := asn1.Marshal(val) - if err != nil { - return - } - - _, err = asn1.Unmarshal(bytes, &raw) - return raw, nil -} - -func makeSafeBags(oid asn1.ObjectIdentifier, value []byte) ([]safeBag, error) { - attribute, err := getLocalKeyId(localKeyId) - - if err != nil { - return nil, EncodeError("local key id: " + err.Error()) - } - - bag := make([]safeBag, 1) - bag[0] = safeBag{ - Id: oid, - Value: asn1.RawValue{Tag: 0, Class: 2, IsCompound: true, Bytes: value}, - Attributes: []pkcs12Attribute{attribute}, - } - - return bag, nil -} - -func makeCertBagContentInfo(derBytes []byte) (*contentInfo, error) { - certBag1 := certBag{ - Id: oidCertTypeX509Certificate, - Data: derBytes, - } - - bytes, err := asn1.Marshal(certBag1) - if err != nil { - return nil, EncodeError("encoding cert bag: " + err.Error()) - } - - certSafeBags, err := makeSafeBags(oidCertBag, bytes) - if err != nil { - return nil, EncodeError("safe bags: " + err.Error()) - } - - return makeContentInfo(certSafeBags) -} - -func makeShroudedKeyBagContentInfo(privateKey interface{}, password []byte) (*contentInfo, error) { - shroudedKeyBagBytes, err := encodePkcs8ShroudedKeyBag(privateKey, password) - if err != nil { - return nil, EncodeError("encode PKCS#8 shrouded key bag: " + err.Error()) - } - - safeBags, err := makeSafeBags(oidPKCS8ShroudedKeyBag, shroudedKeyBagBytes) - if err != nil { - return nil, EncodeError("safe bags: " + err.Error()) - } - - return makeContentInfo(safeBags) -} - -func makeContentInfo(val interface{}) (*contentInfo, error) { - fullBytes, err := asn1.Marshal(val) - if err != nil { - return nil, EncodeError("contentInfo raw value marshal: " + err.Error()) - } - - octetStringVal := asn1.RawValue{Tag: 4, Class: 0, IsCompound: false, Bytes: fullBytes} - octetStringFullBytes, err := asn1.Marshal(octetStringVal) - if err != nil { - return nil, EncodeError("raw contentInfo to octet string: " + err.Error()) - } - - contentInfo := contentInfo{ContentType: oidDataContentType} - contentInfo.Content = asn1.RawValue{Tag: 0, Class: 2, IsCompound: true, Bytes: octetStringFullBytes} - - return &contentInfo, nil -} - -func makeContentInfos(derBytes []byte, privateKey interface{}, password []byte) ([]contentInfo, error) { - shroudedKeyContentInfo, err := makeShroudedKeyBagContentInfo(privateKey, password) - if err != nil { - return nil, EncodeError("shrouded key content info: " + err.Error()) - } - - certBagContentInfo, err := makeCertBagContentInfo(derBytes) - if err != nil { - return nil, EncodeError("cert bag content info: " + err.Error()) - } - - contentInfos := make([]contentInfo, 2) - contentInfos[0] = *shroudedKeyContentInfo - contentInfos[1] = *certBagContentInfo - - return contentInfos, nil -} - -func makeSalt(saltByteCount int) ([]byte, error) { - salt := make([]byte, saltByteCount) - _, err := io.ReadFull(rand.Reader, salt) - return salt, err -} - -// Encode converts a certificate and a private key to the PKCS#12 byte stream format. -// -// derBytes is a DER encoded certificate. -// privateKey is an RSA -func Encode(derBytes []byte, privateKey interface{}, password string) (pfxBytes []byte, err error) { - secret, err := bmpString(password) - if err != nil { - return nil, ErrIncorrectPassword - } - - contentInfos, err := makeContentInfos(derBytes, privateKey, secret) - if err != nil { - return nil, err - } - - // Marshal []contentInfo so we can re-constitute the byte stream that will - // be suitable for computing the MAC - bytes, err := asn1.Marshal(contentInfos) - if err != nil { - return nil, err - } - - // Unmarshal as an asn1.RawValue so, we can compute the MAC against the .Bytes - var contentInfosRaw asn1.RawValue - err = unmarshal(bytes, &contentInfosRaw) - if err != nil { - return nil, err - } - - authSafeContentInfo, err := makeContentInfo(contentInfosRaw) - if err != nil { - return nil, EncodeError("authSafe content info: " + err.Error()) - } - - salt, err := makeSalt(pbeSaltSizeBytes) - if err != nil { - return nil, EncodeError("salt value: " + err.Error()) - } - - // Compute the MAC for marshaled bytes of contentInfos, which includes the - // cert bag, and the shrouded key bag. - digest := computeMac(contentInfosRaw.FullBytes, pbeIterationCount, salt, secret) - - pfx := pfxPdu{ - Version: 3, - AuthSafe: *authSafeContentInfo, - MacData: macData{ - Iterations: pbeIterationCount, - MacSalt: salt, - Mac: digestInfo{ - Algorithm: pkix.AlgorithmIdentifier{ - Algorithm: oidSHA1, - }, - Digest: digest, - }, - }, - } - - bytes, err = asn1.Marshal(pfx) - if err != nil { - return nil, EncodeError("marshal PFX PDU: " + err.Error()) - } - - return bytes, err -} - -func getSafeContents(p12Data, password []byte) (bags []safeBag, updatedPassword []byte, err error) { - pfx := new(pfxPdu) - - if err := unmarshal(p12Data, pfx); err != nil { - return nil, nil, errors.New("pkcs12: error reading P12 data: " + err.Error()) - } - - if pfx.Version != 3 { - return nil, nil, NotImplementedError("can only decode v3 PFX PDU's") - } - - if !pfx.AuthSafe.ContentType.Equal(oidDataContentType) { - return nil, nil, NotImplementedError("only password-protected PFX is implemented") - } - - // unmarshal the explicit bytes in the content for type 'data' - if err := unmarshal(pfx.AuthSafe.Content.Bytes, &pfx.AuthSafe.Content); err != nil { - return nil, nil, err - } - - if len(pfx.MacData.Mac.Algorithm.Algorithm) == 0 { - return nil, nil, errors.New("pkcs12: no MAC in data") - } - - if err := verifyMac(&pfx.MacData, pfx.AuthSafe.Content.Bytes, password); err != nil { - if err == ErrIncorrectPassword && len(password) == 2 && password[0] == 0 && password[1] == 0 { - // some implementations use an empty byte array - // for the empty string password try one more - // time with empty-empty password - password = nil - err = verifyMac(&pfx.MacData, pfx.AuthSafe.Content.Bytes, password) - } - if err != nil { - return nil, nil, err - } - } - - var authenticatedSafe []contentInfo - if err := unmarshal(pfx.AuthSafe.Content.Bytes, &authenticatedSafe); err != nil { - return nil, nil, err - } - - if len(authenticatedSafe) != 2 { - return nil, nil, NotImplementedError("expected exactly two items in the authenticated safe") - } - - for _, ci := range authenticatedSafe { - var data []byte - - switch { - case ci.ContentType.Equal(oidDataContentType): - if err := unmarshal(ci.Content.Bytes, &data); err != nil { - return nil, nil, err - } - case ci.ContentType.Equal(oidEncryptedDataContentType): - var encryptedData encryptedData - if err := unmarshal(ci.Content.Bytes, &encryptedData); err != nil { - return nil, nil, err - } - if encryptedData.Version != 0 { - return nil, nil, NotImplementedError("only version 0 of EncryptedData is supported") - } - if data, err = pbDecrypt(encryptedData.EncryptedContentInfo, password); err != nil { - return nil, nil, err - } - default: - return nil, nil, NotImplementedError("only data and encryptedData content types are supported in authenticated safe") - } - - var safeContents []safeBag - if err := unmarshal(data, &safeContents); err != nil { - return nil, nil, err - } - - bags = append(bags, safeContents...) - } - - return bags, password, nil -} diff --git a/builder/azure/pkcs12/pkcs12_test.go b/builder/azure/pkcs12/pkcs12_test.go deleted file mode 100644 index 2ac382a0b..000000000 --- a/builder/azure/pkcs12/pkcs12_test.go +++ /dev/null @@ -1,247 +0,0 @@ -// Copyright 2015 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package pkcs12 - -import ( - "crypto/ecdsa" - "crypto/elliptic" - "crypto/rand" - "crypto/rsa" - "crypto/tls" - "crypto/x509" - "crypto/x509/pkix" - "encoding/base64" - "encoding/pem" - "fmt" - "math/big" - "testing" - "time" -) - -func TestPfx(t *testing.T) { - for commonName, base64P12 := range testdata { - p12, _ := base64.StdEncoding.DecodeString(base64P12) - - priv, cert, err := Decode(p12, "") - if err != nil { - t.Fatal(err) - } - - if err := priv.(*rsa.PrivateKey).Validate(); err != nil { - t.Errorf("error while validating private key: %v", err) - } - - if cert.Subject.CommonName != commonName { - t.Errorf("expected common name to be %q, but found %q", commonName, cert.Subject.CommonName) - } - } -} - -func TestPfxRoundTriRsa(t *testing.T) { - privateKey, err := rsa.GenerateKey(rand.Reader, 512) - if err != nil { - t.Fatal(err.Error()) - } - - key := testPfxRoundTrip(t, privateKey) - - actualPrivateKey, ok := key.(*rsa.PrivateKey) - if !ok { - t.Fatalf("failed to decode private key") - } - - if privateKey.D.Cmp(actualPrivateKey.D) != 0 { - t.Errorf("priv.D") - } -} - -func TestPfxRoundTriEcdsa(t *testing.T) { - privateKey, err := ecdsa.GenerateKey(elliptic.P224(), rand.Reader) - if err != nil { - t.Fatal(err.Error()) - } - - key := testPfxRoundTrip(t, privateKey) - - actualPrivateKey, ok := key.(*ecdsa.PrivateKey) - if !ok { - t.Fatalf("failed to decode private key") - } - - if privateKey.D.Cmp(actualPrivateKey.D) != 0 { - t.Errorf("priv.D") - } -} - -func testPfxRoundTrip(t *testing.T, privateKey interface{}) interface{} { - certificateBytes, err := newCertificate("hostname", privateKey) - if err != nil { - t.Fatal(err.Error()) - } - - bytes, err := Encode(certificateBytes, privateKey, "sesame") - if err != nil { - t.Fatal(err.Error()) - } - - key, _, err := Decode(bytes, "sesame") - if err != nil { - t.Fatalf(err.Error()) - } - - return key -} - -func TestPEM(t *testing.T) { - for commonName, base64P12 := range testdata { - p12, _ := base64.StdEncoding.DecodeString(base64P12) - - blocks, err := ToPEM(p12, "") - if err != nil { - t.Fatalf("error while converting to PEM: %s", err) - } - - var pemData []byte - for _, b := range blocks { - pemData = append(pemData, pem.EncodeToMemory(b)...) - } - - cert, err := tls.X509KeyPair(pemData, pemData) - if err != nil { - t.Errorf("err while converting to key pair: %v", err) - } - config := tls.Config{ - Certificates: []tls.Certificate{cert}, - } - config.BuildNameToCertificate() - - if _, exists := config.NameToCertificate[commonName]; !exists { - t.Errorf("did not find our cert in PEM?: %v", config.NameToCertificate) - } - } -} - -func ExampleToPEM() { - p12, _ := base64.StdEncoding.DecodeString(`MIIJzgIBAzCCCZQGCS ... CA+gwggPk==`) - - blocks, err := ToPEM(p12, "password") - if err != nil { - panic(err) - } - - var pemData []byte - for _, b := range blocks { - pemData = append(pemData, pem.EncodeToMemory(b)...) - } - - // then use PEM data for tls to construct tls certificate: - cert, err := tls.X509KeyPair(pemData, pemData) - if err != nil { - panic(err) - } - - config := &tls.Config{ - Certificates: []tls.Certificate{cert}, - } - - _ = config -} - -func newCertificate(hostname string, privateKey interface{}) ([]byte, error) { - t, _ := time.Parse("2006-01-02", "2016-01-01") - notBefore := t - notAfter := notBefore.Add(365 * 24 * time.Hour) - - serialNumber, err := rand.Int(rand.Reader, new(big.Int).Lsh(big.NewInt(1), 128)) - if err != nil { - err := fmt.Errorf("Failed to Generate Serial Number: %v", err) - return nil, err - } - - template := x509.Certificate{ - SerialNumber: serialNumber, - Issuer: pkix.Name{ - CommonName: hostname, - }, - Subject: pkix.Name{ - CommonName: hostname, - }, - - NotBefore: notBefore, - NotAfter: notAfter, - - KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, - ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, - BasicConstraintsValid: true, - } - - var publicKey interface{} - switch key := privateKey.(type) { - case *rsa.PrivateKey: - publicKey = key.Public() - case *ecdsa.PrivateKey: - publicKey = key.Public() - default: - panic(fmt.Sprintf("unsupported private key type: %T", privateKey)) - } - - derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, publicKey, privateKey) - if err != nil { - return nil, fmt.Errorf("Failed to Generate derBytes: " + err.Error()) - } - - return derBytes, nil -} - -var testdata = map[string]string{ - // 'null' password test case - "Windows Azure Tools": `MIIKDAIBAzCCCcwGCSqGSIb3DQEHAaCCCb0Eggm5MIIJtTCCBe4GCSqGSIb3DQEHAaCCBd8EggXbMIIF1zCCBdMGCyqGSIb3DQEMCgECoIIE7jCCBOowHAYKKoZIhvcNAQwBAzAOBAhStUNnlTGV+gICB9AEggTIJ81JIossF6boFWpPtkiQRPtI6DW6e9QD4/WvHAVrM2bKdpMzSMsCML5NyuddANTKHBVq00Jc9keqGNAqJPKkjhSUebzQFyhe0E1oI9T4zY5UKr/I8JclOeccH4QQnsySzYUG2SnniXnQ+JrG3juetli7EKth9h6jLc6xbubPadY5HMB3wL/eG/kJymiXwU2KQ9Mgd4X6jbcV+NNCE/8jbZHvSTCPeYTJIjxfeX61Sj5kFKUCzERbsnpyevhY3X0eYtEDezZQarvGmXtMMdzf8HJHkWRdk9VLDLgjk8uiJif/+X4FohZ37ig0CpgC2+dP4DGugaZZ51hb8tN9GeCKIsrmWogMXDIVd0OACBp/EjJVmFB6y0kUCXxUE0TZt0XA1tjAGJcjDUpBvTntZjPsnH/4ZySy+s2d9OOhJ6pzRQBRm360TzkFdSwk9DLiLdGfv4pwMMu/vNGBlqjP/1sQtj+jprJiD1sDbCl4AdQZVoMBQHadF2uSD4/o17XG/Ci0r2h6Htc2yvZMAbEY4zMjjIn2a+vqIxD6onexaek1R3zbkS9j19D6EN9EWn8xgz80YRCyW65znZk8xaIhhvlU/mg7sTxeyuqroBZNcq6uDaQTehDpyH7bY2l4zWRpoj10a6JfH2q5shYz8Y6UZC/kOTfuGqbZDNZWro/9pYquvNNW0M847E5t9bsf9VkAAMHRGBbWoVoU9VpI0UnoXSfvpOo+aXa2DSq5sHHUTVY7A9eov3z5IqT+pligx11xcs+YhDWcU8di3BTJisohKvv5Y8WSkm/rloiZd4ig269k0jTRk1olP/vCksPli4wKG2wdsd5o42nX1yL7mFfXocOANZbB+5qMkiwdyoQSk+Vq+C8nAZx2bbKhUq2MbrORGMzOe0Hh0x2a0PeObycN1Bpyv7Mp3ZI9h5hBnONKCnqMhtyQHUj/nNvbJUnDVYNfoOEqDiEqqEwB7YqWzAKz8KW0OIqdlM8uiQ4JqZZlFllnWJUfaiDrdFM3lYSnFQBkzeVlts6GpDOOBjCYd7dcCNS6kq6pZC6p6HN60Twu0JnurZD6RT7rrPkIGE8vAenFt4iGe/yF52fahCSY8Ws4K0UTwN7bAS+4xRHVCWvE8sMRZsRCHizb5laYsVrPZJhE6+hux6OBb6w8kwPYXc+ud5v6UxawUWgt6uPwl8mlAtU9Z7Miw4Nn/wtBkiLL/ke1UI1gqJtcQXgHxx6mzsjh41+nAgTvdbsSEyU6vfOmxGj3Rwc1eOrIhJUqn5YjOWfzzsz/D5DzWKmwXIwdspt1p+u+kol1N3f2wT9fKPnd/RGCb4g/1hc3Aju4DQYgGY782l89CEEdalpQ/35bQczMFk6Fje12HykakWEXd/bGm9Unh82gH84USiRpeOfQvBDYoqEyrY3zkFZzBjhDqa+jEcAj41tcGx47oSfDq3iVYCdL7HSIjtnyEktVXd7mISZLoMt20JACFcMw+mrbjlug+eU7o2GR7T+LwtOp/p4LZqyLa7oQJDwde1BNZtm3TCK2P1mW94QDL0nDUps5KLtr1DaZXEkRbjSJub2ZE9WqDHyU3KA8G84Tq/rN1IoNu/if45jacyPje1Npj9IftUZSP22nV7HMwZtwQ4P4MYHRMBMGCSqGSIb3DQEJFTEGBAQBAAAAMFsGCSqGSIb3DQEJFDFOHkwAewBCADQAQQA0AEYARQBCADAALQBBADEAOABBAC0ANAA0AEIAQgAtAEIANQBGADIALQA0ADkAMQBFAEYAMQA1ADIAQgBBADEANgB9MF0GCSsGAQQBgjcRATFQHk4ATQBpAGMAcgBvAHMAbwBmAHQAIABTAG8AZgB0AHcAYQByAGUAIABLAGUAeQAgAFMAdABvAHIAYQBnAGUAIABQAHIAbwB2AGkAZABlAHIwggO/BgkqhkiG9w0BBwagggOwMIIDrAIBADCCA6UGCSqGSIb3DQEHATAcBgoqhkiG9w0BDAEGMA4ECEBk5ZAYpu0WAgIH0ICCA3hik4mQFGpw9Ha8TQPtk+j2jwWdxfF0+sTk6S8PTsEfIhB7wPltjiCK92Uv2tCBQnodBUmatIfkpnRDEySmgmdglmOCzj204lWAMRs94PoALGn3JVBXbO1vIDCbAPOZ7Z0Hd0/1t2hmk8v3//QJGUg+qr59/4y/MuVfIg4qfkPcC2QSvYWcK3oTf6SFi5rv9B1IOWFgN5D0+C+x/9Lb/myPYX+rbOHrwtJ4W1fWKoz9g7wwmGFA9IJ2DYGuH8ifVFbDFT1Vcgsvs8arSX7oBsJVW0qrP7XkuDRe3EqCmKW7rBEwYrFznhxZcRDEpMwbFoSvgSIZ4XhFY9VKYglT+JpNH5iDceYEBOQL4vBLpxNUk3l5jKaBNxVa14AIBxq18bVHJ+STInhLhad4u10v/Xbx7wIL3f9DX1yLAkPrpBYbNHS2/ew6H/ySDJnoIDxkw2zZ4qJ+qUJZ1S0lbZVG+VT0OP5uF6tyOSpbMlcGkdl3z254n6MlCrTifcwkzscysDsgKXaYQw06rzrPW6RDub+t+hXzGny799fS9jhQMLDmOggaQ7+LA4oEZsfT89HLMWxJYDqjo3gIfjciV2mV54R684qLDS+AO09U49e6yEbwGlq8lpmO/pbXCbpGbB1b3EomcQbxdWxW2WEkkEd/VBn81K4M3obmywwXJkw+tPXDXfBmzzaqqCR+onMQ5ME1nMkY8ybnfoCc1bDIupjVWsEL2Wvq752RgI6KqzVNr1ew1IdqV5AWN2fOfek+0vi3Jd9FHF3hx8JMwjJL9dZsETV5kHtYJtE7wJ23J68BnCt2eI0GEuwXcCf5EdSKN/xXCTlIokc4Qk/gzRdIZsvcEJ6B1lGovKG54X4IohikqTjiepjbsMWj38yxDmK3mtENZ9ci8FPfbbvIEcOCZIinuY3qFUlRSbx7VUerEoV1IP3clUwexVQo4lHFee2jd7ocWsdSqSapW7OWUupBtDzRkqVhE7tGria+i1W2d6YLlJ21QTjyapWJehAMO637OdbJCCzDs1cXbodRRE7bsP492ocJy8OX66rKdhYbg8srSFNKdb3pF3UDNbN9jhI/t8iagRhNBhlQtTr1me2E/c86Q18qcRXl4bcXTt6acgCeffK6Y26LcVlrgjlD33AEYRRUeyC+rpxbT0aMjdFderlndKRIyG23mSp0HaUwNzAfMAcGBSsOAwIaBBRlviCbIyRrhIysg2dc/KbLFTc2vQQUg4rfwHMM4IKYRD/fsd1x6dda+wQ=`, - // empty string password test case - "testing@example.com": `MIIJzgIBAzCCCZQGCSqGSIb3DQEHAaCCCYUEggmBMIIJfTCCA/cGCSqGSIb3DQEHBqCCA+gwggPk -AgEAMIID3QYJKoZIhvcNAQcBMBwGCiqGSIb3DQEMAQYwDgQIIszfRGqcmPcCAggAgIIDsOZ9Eg1L -s5Wx8JhYoV3HAL4aRnkAWvTYB5NISZOgSgIQTssmt/3A7134dibTmaT/93LikkL3cTKLnQzJ4wDf -YZ1bprpVJvUqz+HFT79m27bP9zYXFrvxWBJbxjYKTSjQMgz+h8LAEpXXGajCmxMJ1oCOtdXkhhzc -LdZN6SAYgtmtyFnCdMEDskSggGuLb3fw84QEJ/Sj6FAULXunW/CPaS7Ce0TMsKmNU/jfFWj3yXXw -ro0kwjKiVLpVFlnBlHo2OoVU7hmkm59YpGhLgS7nxLD3n7nBroQ0ID1+8R01NnV9XLGoGzxMm1te -6UyTCkr5mj+kEQ8EP1Ys7g/TC411uhVWySMt/rcpkx7Vz1r9kYEAzJpONAfr6cuEVkPKrxpq4Fh0 -2fzlKBky0i/hrfIEUmngh+ERHUb/Mtv/fkv1j5w9suESbhsMLLiCXAlsP1UWMX+3bNizi3WVMEts -FM2k9byn+p8IUD/A8ULlE4kEaWeoc+2idkCNQkLGuIdGUXUFVm58se0auUkVRoRJx8x4CkMesT8j -b1H831W66YRWoEwwDQp2kK1lA2vQXxdVHWlFevMNxJeromLzj3ayiaFrfByeUXhR2S+Hpm+c0yNR -4UVU9WED2kacsZcpRm9nlEa5sr28mri5JdBrNa/K02OOhvKCxr5ZGmbOVzUQKla2z4w+Ku9k8POm -dfDNU/fGx1b5hcFWtghXe3msWVsSJrQihnN6q1ughzNiYZlJUGcHdZDRtiWwCFI0bR8h/Dmg9uO9 -4rawQQrjIRT7B8yF3UbkZyAqs8Ppb1TsMeNPHh1rxEfGVQknh/48ouJYsmtbnzugTUt3mJCXXiL+ -XcPMV6bBVAUu4aaVKSmg9+yJtY4/VKv10iw88ktv29fViIdBe3t6l/oPuvQgbQ8dqf4T8w0l/uKZ -9lS1Na9jfT1vCoS7F5TRi+tmyj1vL5kr/amEIW6xKEP6oeAMvCMtbPAzVEj38zdJ1R22FfuIBxkh -f0Zl7pdVbmzRxl/SBx9iIBJSqAvcXItiT0FIj8HxQ+0iZKqMQMiBuNWJf5pYOLWGrIyntCWwHuaQ -wrx0sTGuEL9YXLEAsBDrsvzLkx/56E4INGZFrH8G7HBdW6iGqb22IMI4GHltYSyBRKbB0gadYTyv -abPEoqww8o7/85aPSzOTJ/53ozD438Q+d0u9SyDuOb60SzCD/zPuCEd78YgtXJwBYTuUNRT27FaM -3LGMX8Hz+6yPNRnmnA2XKPn7dx/IlaqAjIs8MIIFfgYJKoZIhvcNAQcBoIIFbwSCBWswggVnMIIF -YwYLKoZIhvcNAQwKAQKgggTuMIIE6jAcBgoqhkiG9w0BDAEDMA4ECJr0cClYqOlcAgIIAASCBMhe -OQSiP2s0/46ONXcNeVAkz2ksW3u/+qorhSiskGZ0b3dFa1hhgBU2Q7JVIkc4Hf7OXaT1eVQ8oqND -uhqsNz83/kqYo70+LS8Hocj49jFgWAKrf/yQkdyP1daHa2yzlEw4mkpqOfnIORQHvYCa8nEApspZ -wVu8y6WVuLHKU67mel7db2xwstQp7PRuSAYqGjTfAylElog8ASdaqqYbYIrCXucF8iF9oVgmb/Qo -xrXshJ9aSLO4MuXlTPELmWgj07AXKSb90FKNihE+y0bWb9LPVFY1Sly3AX9PfrtkSXIZwqW3phpv -MxGxQl/R6mr1z+hlTfY9Wdpb5vlKXPKA0L0Rt8d2pOesylFi6esJoS01QgP1kJILjbrV731kvDc0 -Jsd+Oxv4BMwA7ClG8w1EAOInc/GrV1MWFGw/HeEqj3CZ/l/0jv9bwkbVeVCiIhoL6P6lVx9pXq4t -KZ0uKg/tk5TVJmG2vLcMLvezD0Yk3G2ZOMrywtmskrwoF7oAUpO9e87szoH6fEvUZlkDkPVW1NV4 -cZk3DBSQiuA3VOOg8qbo/tx/EE3H59P0axZWno2GSB0wFPWd1aj+b//tJEJHaaNR6qPRj4IWj9ru -Qbc8eRAcVWleHg8uAehSvUXlFpyMQREyrnpvMGddpiTC8N4UMrrBRhV7+UbCOWhxPCbItnInBqgl -1JpSZIP7iUtsIMdu3fEC2cdbXMTRul+4rdzUR7F9OaezV3jjvcAbDvgbK1CpyC+MJ1Mxm/iTgk9V -iUArydhlR8OniN84GyGYoYCW9O/KUwb6ASmeFOu/msx8x6kAsSQHIkKqMKv0TUR3kZnkxUvdpBGP -KTl4YCTvNGX4dYALBqrAETRDhua2KVBD/kEttDHwBNVbN2xi81+Mc7ml461aADfk0c66R/m2sjHB -2tN9+wG12OIWFQjL6wF/UfJMYamxx2zOOExiId29Opt57uYiNVLOO4ourPewHPeH0u8Gz35aero7 -lkt7cZAe1Q0038JUuE/QGlnK4lESK9UkSIQAjSaAlTsrcfwtQxB2EjoOoLhwH5mvxUEmcNGNnXUc -9xj3M5BD3zBz3Ft7G3YMMDwB1+zC2l+0UG0MGVjMVaeoy32VVNvxgX7jk22OXG1iaOB+PY9kdk+O -X+52BGSf/rD6X0EnqY7XuRPkMGgjtpZeAYxRQnFtCZgDY4wYheuxqSSpdF49yNczSPLkgB3CeCfS -+9NTKN7aC6hBbmW/8yYh6OvSiCEwY0lFS/T+7iaVxr1loE4zI1y/FFp4Pe1qfLlLttVlkygga2UU -SCunTQ8UB/M5IXWKkhMOO11dP4niWwb39Y7pCWpau7mwbXOKfRPX96cgHnQJK5uG+BesDD1oYnX0 -6frN7FOnTSHKruRIwuI8KnOQ/I+owmyz71wiv5LMQt+yM47UrEjB/EZa5X8dpEwOZvkdqL7utcyo -l0XH5kWMXdW856LL/FYftAqJIDAmtX1TXF/rbP6mPyN/IlDC0gjP84Uzd/a2UyTIWr+wk49Ek3vQ -/uDamq6QrwAxVmNh5Tset5Vhpc1e1kb7mRMZIzxSP8JcTuYd45oFKi98I8YjvueHVZce1g7OudQP -SbFQoJvdT46iBg1TTatlltpOiH2mFaxWVS0xYjAjBgkqhkiG9w0BCRUxFgQUdA9eVqvETX4an/c8 -p8SsTugkit8wOwYJKoZIhvcNAQkUMS4eLABGAHIAaQBlAG4AZABsAHkAIABuAGEAbQBlACAAZgBv -AHIAIABjAGUAcgB0MDEwITAJBgUrDgMCGgUABBRFsNz3Zd1O1GI8GTuFwCWuDOjEEwQIuBEfIcAy -HQ8CAggA`, -} diff --git a/builder/azure/pkcs12/pkcs8.go b/builder/azure/pkcs12/pkcs8.go deleted file mode 100644 index 22ab14d25..000000000 --- a/builder/azure/pkcs12/pkcs8.go +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright 2016 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. -package pkcs12 - -import ( - "crypto/ecdsa" - "crypto/rsa" - "crypto/x509" - "crypto/x509/pkix" - "encoding/asn1" - "errors" -) - -// pkcs8 reflects an ASN.1, PKCS#8 PrivateKey. See -// ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-8/pkcs-8v1_2.asn -// and RFC5208. -type pkcs8 struct { - Version int - Algo pkix.AlgorithmIdentifier - PrivateKey []byte - // optional attributes omitted. -} - -var ( - oidPublicKeyRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 1} - oidPublicKeyECDSA = asn1.ObjectIdentifier{1, 2, 840, 10045, 2, 1} - - nullAsn = asn1.RawValue{Tag: 5} -) - -// marshalPKCS8PrivateKey converts a private key to PKCS#8 encoded form. -// See http://www.rsa.com/rsalabs/node.asp?id=2130 and RFC5208. -func marshalPKCS8PrivateKey(key interface{}) (der []byte, err error) { - pkcs := pkcs8{ - Version: 0, - } - - switch key := key.(type) { - case *rsa.PrivateKey: - pkcs.Algo = pkix.AlgorithmIdentifier{ - Algorithm: oidPublicKeyRSA, - Parameters: nullAsn, - } - pkcs.PrivateKey = x509.MarshalPKCS1PrivateKey(key) - case *ecdsa.PrivateKey: - bytes, err := x509.MarshalECPrivateKey(key) - if err != nil { - return nil, errors.New("x509: failed to marshal to PKCS#8: " + err.Error()) - } - - pkcs.Algo = pkix.AlgorithmIdentifier{ - Algorithm: oidPublicKeyECDSA, - Parameters: nullAsn, - } - pkcs.PrivateKey = bytes - default: - return nil, errors.New("x509: PKCS#8 only RSA and ECDSA private keys supported") - } - - bytes, err := asn1.Marshal(pkcs) - if err != nil { - return nil, errors.New("x509: failed to marshal to PKCS#8: " + err.Error()) - } - - return bytes, nil -} diff --git a/builder/azure/pkcs12/pkcs8_test.go b/builder/azure/pkcs12/pkcs8_test.go deleted file mode 100644 index 7d12119f5..000000000 --- a/builder/azure/pkcs12/pkcs8_test.go +++ /dev/null @@ -1,141 +0,0 @@ -// Copyright 2016 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. -package pkcs12 - -import ( - "crypto/ecdsa" - "crypto/elliptic" - "crypto/rand" - "crypto/rsa" - "crypto/x509" - "encoding/asn1" - "testing" -) - -func TestRoundTripPkcs8Rsa(t *testing.T) { - privateKey, err := rsa.GenerateKey(rand.Reader, 512) - if err != nil { - t.Fatalf("failed to generate a private key: %s", err) - } - - bytes, err := marshalPKCS8PrivateKey(privateKey) - if err != nil { - t.Fatalf("failed to marshal private key: %s", err) - } - - key, err := x509.ParsePKCS8PrivateKey(bytes) - if err != nil { - t.Fatalf("failed to parse private key: %s", err) - } - - actualPrivateKey, ok := key.(*rsa.PrivateKey) - if !ok { - t.Fatalf("expected key to be of type *rsa.PrivateKey, but actual was %T", key) - } - - if actualPrivateKey.Validate() != nil { - t.Fatalf("private key did not validate") - } - - if actualPrivateKey.N.Cmp(privateKey.N) != 0 { - t.Errorf("private key's N did not round trip") - } - if actualPrivateKey.D.Cmp(privateKey.D) != 0 { - t.Errorf("private key's D did not round trip") - } - if actualPrivateKey.E != privateKey.E { - t.Errorf("private key's E did not round trip") - } - if actualPrivateKey.Primes[0].Cmp(privateKey.Primes[0]) != 0 { - t.Errorf("private key's P did not round trip") - } - if actualPrivateKey.Primes[1].Cmp(privateKey.Primes[1]) != 0 { - t.Errorf("private key's Q did not round trip") - } -} - -func TestRoundTripPkcs8Ecdsa(t *testing.T) { - privateKey, err := ecdsa.GenerateKey(elliptic.P224(), rand.Reader) - if err != nil { - t.Fatalf("failed to generate a private key: %s", err) - } - - bytes, err := marshalPKCS8PrivateKey(privateKey) - if err != nil { - t.Fatalf("failed to marshal private key: %s", err) - } - - key, err := x509.ParsePKCS8PrivateKey(bytes) - if err != nil { - t.Fatalf("failed to parse private key: %s", err) - } - - actualPrivateKey, ok := key.(*ecdsa.PrivateKey) - if !ok { - t.Fatalf("expected key to be of type *ecdsa.PrivateKey, but actual was %T", key) - } - - // sanity check, not exhaustive - if actualPrivateKey.D.Cmp(privateKey.D) != 0 { - t.Errorf("private key's D did not round trip") - } - if actualPrivateKey.X.Cmp(privateKey.X) != 0 { - t.Errorf("private key's X did not round trip") - } - if actualPrivateKey.Y.Cmp(privateKey.Y) != 0 { - t.Errorf("private key's Y did not round trip") - } - if actualPrivateKey.Curve.Params().B.Cmp(privateKey.Curve.Params().B) != 0 { - t.Errorf("private key's Curve.B did not round trip") - } -} - -func TestNullParametersPkcs8Rsa(t *testing.T) { - privateKey, err := rsa.GenerateKey(rand.Reader, 512) - if err != nil { - t.Fatalf("failed to generate a private key: %s", err) - } - - checkNullParameter(t, privateKey) -} - -func TestNullParametersPkcs8Ecdsa(t *testing.T) { - privateKey, err := ecdsa.GenerateKey(elliptic.P224(), rand.Reader) - if err != nil { - t.Fatalf("failed to generate a private key: %s", err) - } - - checkNullParameter(t, privateKey) -} - -func checkNullParameter(t *testing.T, privateKey interface{}) { - bytes, err := marshalPKCS8PrivateKey(privateKey) - if err != nil { - t.Fatalf("failed to marshal private key: %s", err) - } - - var pkcs pkcs8 - rest, err := asn1.Unmarshal(bytes, &pkcs) - if err != nil { - t.Fatalf("failed to unmarshal PKCS#8: %s", err) - } - - if len(rest) != 0 { - t.Fatalf("unexpected trailing bytes of len=%d, bytes=%x", len(rest), rest) - } - - // Only version == 0 is known and valid - if pkcs.Version != 0 { - t.Errorf("expected version=0, but actual=%d", pkcs.Version) - } - - // ensure a NULL parameter is inserted - if pkcs.Algo.Parameters.Tag != 5 { - t.Errorf("expected parameters to be NULL, but actual tag=%d, class=%d, isCompound=%t, bytes=%x", - pkcs.Algo.Parameters.Tag, - pkcs.Algo.Parameters.Class, - pkcs.Algo.Parameters.IsCompound, - pkcs.Algo.Parameters.Bytes) - } -} diff --git a/builder/azure/pkcs12/rc2/bench_test.go b/builder/azure/pkcs12/rc2/bench_test.go deleted file mode 100644 index 3347f338c..000000000 --- a/builder/azure/pkcs12/rc2/bench_test.go +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2015 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package rc2 - -import ( - "testing" -) - -func BenchmarkEncrypt(b *testing.B) { - r, _ := New([]byte{0, 0, 0, 0, 0, 0, 0, 0}, 64) - b.ResetTimer() - var src [8]byte - for i := 0; i < b.N; i++ { - r.Encrypt(src[:], src[:]) - } -} - -func BenchmarkDecrypt(b *testing.B) { - r, _ := New([]byte{0, 0, 0, 0, 0, 0, 0, 0}, 64) - b.ResetTimer() - var src [8]byte - for i := 0; i < b.N; i++ { - r.Decrypt(src[:], src[:]) - } -} diff --git a/builder/azure/pkcs12/rc2/rc2.go b/builder/azure/pkcs12/rc2/rc2.go deleted file mode 100644 index 8c7090258..000000000 --- a/builder/azure/pkcs12/rc2/rc2.go +++ /dev/null @@ -1,274 +0,0 @@ -// Copyright 2015 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package rc2 implements the RC2 cipher -/* -https://www.ietf.org/rfc/rfc2268.txt -http://people.csail.mit.edu/rivest/pubs/KRRR98.pdf - -This code is licensed under the MIT license. -*/ -package rc2 - -import ( - "crypto/cipher" - "encoding/binary" -) - -// The rc2 block size in bytes -const BlockSize = 8 - -type rc2Cipher struct { - k [64]uint16 -} - -// New returns a new rc2 cipher with the given key and effective key length t1 -func New(key []byte, t1 int) (cipher.Block, error) { - // TODO(dgryski): error checking for key length - return &rc2Cipher{ - k: expandKey(key, t1), - }, nil -} - -func (*rc2Cipher) BlockSize() int { return BlockSize } - -var piTable = [256]byte{ - 0xd9, 0x78, 0xf9, 0xc4, 0x19, 0xdd, 0xb5, 0xed, 0x28, 0xe9, 0xfd, 0x79, 0x4a, 0xa0, 0xd8, 0x9d, - 0xc6, 0x7e, 0x37, 0x83, 0x2b, 0x76, 0x53, 0x8e, 0x62, 0x4c, 0x64, 0x88, 0x44, 0x8b, 0xfb, 0xa2, - 0x17, 0x9a, 0x59, 0xf5, 0x87, 0xb3, 0x4f, 0x13, 0x61, 0x45, 0x6d, 0x8d, 0x09, 0x81, 0x7d, 0x32, - 0xbd, 0x8f, 0x40, 0xeb, 0x86, 0xb7, 0x7b, 0x0b, 0xf0, 0x95, 0x21, 0x22, 0x5c, 0x6b, 0x4e, 0x82, - 0x54, 0xd6, 0x65, 0x93, 0xce, 0x60, 0xb2, 0x1c, 0x73, 0x56, 0xc0, 0x14, 0xa7, 0x8c, 0xf1, 0xdc, - 0x12, 0x75, 0xca, 0x1f, 0x3b, 0xbe, 0xe4, 0xd1, 0x42, 0x3d, 0xd4, 0x30, 0xa3, 0x3c, 0xb6, 0x26, - 0x6f, 0xbf, 0x0e, 0xda, 0x46, 0x69, 0x07, 0x57, 0x27, 0xf2, 0x1d, 0x9b, 0xbc, 0x94, 0x43, 0x03, - 0xf8, 0x11, 0xc7, 0xf6, 0x90, 0xef, 0x3e, 0xe7, 0x06, 0xc3, 0xd5, 0x2f, 0xc8, 0x66, 0x1e, 0xd7, - 0x08, 0xe8, 0xea, 0xde, 0x80, 0x52, 0xee, 0xf7, 0x84, 0xaa, 0x72, 0xac, 0x35, 0x4d, 0x6a, 0x2a, - 0x96, 0x1a, 0xd2, 0x71, 0x5a, 0x15, 0x49, 0x74, 0x4b, 0x9f, 0xd0, 0x5e, 0x04, 0x18, 0xa4, 0xec, - 0xc2, 0xe0, 0x41, 0x6e, 0x0f, 0x51, 0xcb, 0xcc, 0x24, 0x91, 0xaf, 0x50, 0xa1, 0xf4, 0x70, 0x39, - 0x99, 0x7c, 0x3a, 0x85, 0x23, 0xb8, 0xb4, 0x7a, 0xfc, 0x02, 0x36, 0x5b, 0x25, 0x55, 0x97, 0x31, - 0x2d, 0x5d, 0xfa, 0x98, 0xe3, 0x8a, 0x92, 0xae, 0x05, 0xdf, 0x29, 0x10, 0x67, 0x6c, 0xba, 0xc9, - 0xd3, 0x00, 0xe6, 0xcf, 0xe1, 0x9e, 0xa8, 0x2c, 0x63, 0x16, 0x01, 0x3f, 0x58, 0xe2, 0x89, 0xa9, - 0x0d, 0x38, 0x34, 0x1b, 0xab, 0x33, 0xff, 0xb0, 0xbb, 0x48, 0x0c, 0x5f, 0xb9, 0xb1, 0xcd, 0x2e, - 0xc5, 0xf3, 0xdb, 0x47, 0xe5, 0xa5, 0x9c, 0x77, 0x0a, 0xa6, 0x20, 0x68, 0xfe, 0x7f, 0xc1, 0xad, -} - -func expandKey(key []byte, t1 int) [64]uint16 { - - l := make([]byte, 128) - copy(l, key) - - var t = len(key) - var t8 = (t1 + 7) / 8 - var tm = byte(255 % uint(1<<(8+uint(t1)-8*uint(t8)))) - - for i := len(key); i < 128; i++ { - l[i] = piTable[l[i-1]+l[uint8(i-t)]] - } - - l[128-t8] = piTable[l[128-t8]&tm] - - for i := 127 - t8; i >= 0; i-- { - l[i] = piTable[l[i+1]^l[i+t8]] - } - - var k [64]uint16 - - for i := range k { - k[i] = uint16(l[2*i]) + uint16(l[2*i+1])*256 - } - - return k -} - -func rotl16(x uint16, b uint) uint16 { - return (x >> (16 - b)) | (x << b) -} - -func (c *rc2Cipher) Encrypt(dst, src []byte) { - - r0 := binary.LittleEndian.Uint16(src[0:]) - r1 := binary.LittleEndian.Uint16(src[2:]) - r2 := binary.LittleEndian.Uint16(src[4:]) - r3 := binary.LittleEndian.Uint16(src[6:]) - - var j int - - for j <= 16 { - // mix r0 - r0 = r0 + c.k[j] + (r3 & r2) + ((^r3) & r1) - r0 = rotl16(r0, 1) - j++ - - // mix r1 - r1 = r1 + c.k[j] + (r0 & r3) + ((^r0) & r2) - r1 = rotl16(r1, 2) - j++ - - // mix r2 - r2 = r2 + c.k[j] + (r1 & r0) + ((^r1) & r3) - r2 = rotl16(r2, 3) - j++ - - // mix r3 - r3 = r3 + c.k[j] + (r2 & r1) + ((^r2) & r0) - r3 = rotl16(r3, 5) - j++ - - } - - r0 = r0 + c.k[r3&63] - r1 = r1 + c.k[r0&63] - r2 = r2 + c.k[r1&63] - r3 = r3 + c.k[r2&63] - - for j <= 40 { - - // mix r0 - r0 = r0 + c.k[j] + (r3 & r2) + ((^r3) & r1) - r0 = rotl16(r0, 1) - j++ - - // mix r1 - r1 = r1 + c.k[j] + (r0 & r3) + ((^r0) & r2) - r1 = rotl16(r1, 2) - j++ - - // mix r2 - r2 = r2 + c.k[j] + (r1 & r0) + ((^r1) & r3) - r2 = rotl16(r2, 3) - j++ - - // mix r3 - r3 = r3 + c.k[j] + (r2 & r1) + ((^r2) & r0) - r3 = rotl16(r3, 5) - j++ - - } - - r0 = r0 + c.k[r3&63] - r1 = r1 + c.k[r0&63] - r2 = r2 + c.k[r1&63] - r3 = r3 + c.k[r2&63] - - for j <= 60 { - - // mix r0 - r0 = r0 + c.k[j] + (r3 & r2) + ((^r3) & r1) - r0 = rotl16(r0, 1) - j++ - - // mix r1 - r1 = r1 + c.k[j] + (r0 & r3) + ((^r0) & r2) - r1 = rotl16(r1, 2) - j++ - - // mix r2 - r2 = r2 + c.k[j] + (r1 & r0) + ((^r1) & r3) - r2 = rotl16(r2, 3) - j++ - - // mix r3 - r3 = r3 + c.k[j] + (r2 & r1) + ((^r2) & r0) - r3 = rotl16(r3, 5) - j++ - } - - binary.LittleEndian.PutUint16(dst[0:], r0) - binary.LittleEndian.PutUint16(dst[2:], r1) - binary.LittleEndian.PutUint16(dst[4:], r2) - binary.LittleEndian.PutUint16(dst[6:], r3) -} - -func (c *rc2Cipher) Decrypt(dst, src []byte) { - - r0 := binary.LittleEndian.Uint16(src[0:]) - r1 := binary.LittleEndian.Uint16(src[2:]) - r2 := binary.LittleEndian.Uint16(src[4:]) - r3 := binary.LittleEndian.Uint16(src[6:]) - - j := 63 - - for j >= 44 { - // unmix r3 - r3 = rotl16(r3, 16-5) - r3 = r3 - c.k[j] - (r2 & r1) - ((^r2) & r0) - j-- - - // unmix r2 - r2 = rotl16(r2, 16-3) - r2 = r2 - c.k[j] - (r1 & r0) - ((^r1) & r3) - j-- - - // unmix r1 - r1 = rotl16(r1, 16-2) - r1 = r1 - c.k[j] - (r0 & r3) - ((^r0) & r2) - j-- - - // unmix r0 - r0 = rotl16(r0, 16-1) - r0 = r0 - c.k[j] - (r3 & r2) - ((^r3) & r1) - j-- - } - - r3 = r3 - c.k[r2&63] - r2 = r2 - c.k[r1&63] - r1 = r1 - c.k[r0&63] - r0 = r0 - c.k[r3&63] - - for j >= 20 { - // unmix r3 - r3 = rotl16(r3, 16-5) - r3 = r3 - c.k[j] - (r2 & r1) - ((^r2) & r0) - j-- - - // unmix r2 - r2 = rotl16(r2, 16-3) - r2 = r2 - c.k[j] - (r1 & r0) - ((^r1) & r3) - j-- - - // unmix r1 - r1 = rotl16(r1, 16-2) - r1 = r1 - c.k[j] - (r0 & r3) - ((^r0) & r2) - j-- - - // unmix r0 - r0 = rotl16(r0, 16-1) - r0 = r0 - c.k[j] - (r3 & r2) - ((^r3) & r1) - j-- - - } - - r3 = r3 - c.k[r2&63] - r2 = r2 - c.k[r1&63] - r1 = r1 - c.k[r0&63] - r0 = r0 - c.k[r3&63] - - for j >= 0 { - - // unmix r3 - r3 = rotl16(r3, 16-5) - r3 = r3 - c.k[j] - (r2 & r1) - ((^r2) & r0) - j-- - - // unmix r2 - r2 = rotl16(r2, 16-3) - r2 = r2 - c.k[j] - (r1 & r0) - ((^r1) & r3) - j-- - - // unmix r1 - r1 = rotl16(r1, 16-2) - r1 = r1 - c.k[j] - (r0 & r3) - ((^r0) & r2) - j-- - - // unmix r0 - r0 = rotl16(r0, 16-1) - r0 = r0 - c.k[j] - (r3 & r2) - ((^r3) & r1) - j-- - - } - - binary.LittleEndian.PutUint16(dst[0:], r0) - binary.LittleEndian.PutUint16(dst[2:], r1) - binary.LittleEndian.PutUint16(dst[4:], r2) - binary.LittleEndian.PutUint16(dst[6:], r3) -} diff --git a/builder/azure/pkcs12/rc2/rc2_test.go b/builder/azure/pkcs12/rc2/rc2_test.go deleted file mode 100644 index 8a49dfaf3..000000000 --- a/builder/azure/pkcs12/rc2/rc2_test.go +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright 2015 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package rc2 - -import ( - "bytes" - "encoding/hex" - "testing" -) - -func TestEncryptDecrypt(t *testing.T) { - - // TODO(dgryski): add the rest of the test vectors from the RFC - var tests = []struct { - key string - plain string - cipher string - t1 int - }{ - { - "0000000000000000", - "0000000000000000", - "ebb773f993278eff", - 63, - }, - { - "ffffffffffffffff", - "ffffffffffffffff", - "278b27e42e2f0d49", - 64, - }, - { - "3000000000000000", - "1000000000000001", - "30649edf9be7d2c2", - 64, - }, - { - "88", - "0000000000000000", - "61a8a244adacccf0", - 64, - }, - { - "88bca90e90875a", - "0000000000000000", - "6ccf4308974c267f", - 64, - }, - { - "88bca90e90875a7f0f79c384627bafb2", - "0000000000000000", - "1a807d272bbe5db1", - 64, - }, - { - "88bca90e90875a7f0f79c384627bafb2", - "0000000000000000", - "2269552ab0f85ca6", - 128, - }, - { - "88bca90e90875a7f0f79c384627bafb216f80a6f85920584c42fceb0be255daf1e", - "0000000000000000", - "5b78d3a43dfff1f1", - 129, - }, - } - - for _, tt := range tests { - k, _ := hex.DecodeString(tt.key) - p, _ := hex.DecodeString(tt.plain) - c, _ := hex.DecodeString(tt.cipher) - - b, _ := New(k, tt.t1) - - var dst [8]byte - - b.Encrypt(dst[:], p) - - if !bytes.Equal(dst[:], c) { - t.Errorf("encrypt failed: got % 2x wanted % 2x\n", dst, c) - } - - b.Decrypt(dst[:], c) - - if !bytes.Equal(dst[:], p) { - t.Errorf("decrypt failed: got % 2x wanted % 2x\n", dst, p) - } - } -} diff --git a/builder/azure/pkcs12/safebags.go b/builder/azure/pkcs12/safebags.go deleted file mode 100644 index 836236da3..000000000 --- a/builder/azure/pkcs12/safebags.go +++ /dev/null @@ -1,105 +0,0 @@ -// Copyright 2015 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package pkcs12 - -import ( - "crypto/x509" - "crypto/x509/pkix" - "encoding/asn1" - "errors" -) - -var ( - // see https://tools.ietf.org/html/rfc7292#appendix-D - oidCertTypeX509Certificate = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 9, 22, 1}) - oidPKCS8ShroudedKeyBag = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 12, 10, 1, 2}) - oidCertBag = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 12, 10, 1, 3}) -) - -type certBag struct { - Id asn1.ObjectIdentifier - Data []byte `asn1:"tag:0,explicit"` -} - -func getAlgorithmParams(salt []byte, iterations int) (asn1.RawValue, error) { - params := pbeParams{ - Salt: salt, - Iterations: iterations, - } - - return convertToRawVal(params) -} - -func encodePkcs8ShroudedKeyBag(privateKey interface{}, password []byte) (bytes []byte, err error) { - privateKeyBytes, err := marshalPKCS8PrivateKey(privateKey) - - if err != nil { - return nil, errors.New("pkcs12: error encoding PKCS#8 private key: " + err.Error()) - } - - salt, err := makeSalt(pbeSaltSizeBytes) - if err != nil { - return nil, errors.New("pkcs12: error creating PKCS#8 salt: " + err.Error()) - } - - pkData, err := pbEncrypt(privateKeyBytes, salt, password, pbeIterationCount) - if err != nil { - return nil, errors.New("pkcs12: error encoding PKCS#8 shrouded key bag when encrypting cert bag: " + err.Error()) - } - - params, err := getAlgorithmParams(salt, pbeIterationCount) - if err != nil { - return nil, errors.New("pkcs12: error encoding PKCS#8 shrouded key bag algorithm's parameters: " + err.Error()) - } - - pkinfo := encryptedPrivateKeyInfo{ - AlgorithmIdentifier: pkix.AlgorithmIdentifier{ - Algorithm: oidPBEWithSHAAnd3KeyTripleDESCBC, - Parameters: params, - }, - EncryptedData: pkData, - } - - bytes, err = asn1.Marshal(pkinfo) - if err != nil { - return nil, errors.New("pkcs12: error encoding PKCS#8 shrouded key bag: " + err.Error()) - } - - return bytes, err -} - -func decodePkcs8ShroudedKeyBag(asn1Data, password []byte) (privateKey interface{}, err error) { - pkinfo := new(encryptedPrivateKeyInfo) - if err = unmarshal(asn1Data, pkinfo); err != nil { - return nil, errors.New("pkcs12: error decoding PKCS#8 shrouded key bag: " + err.Error()) - } - - pkData, err := pbDecrypt(pkinfo, password) - if err != nil { - return nil, errors.New("pkcs12: error decrypting PKCS#8 shrouded key bag: " + err.Error()) - } - - ret := new(asn1.RawValue) - if err = unmarshal(pkData, ret); err != nil { - return nil, errors.New("pkcs12: error unmarshalling decrypted private key: " + err.Error()) - } - - if privateKey, err = x509.ParsePKCS8PrivateKey(pkData); err != nil { - return nil, errors.New("pkcs12: error parsing PKCS#8 private key: " + err.Error()) - } - - return privateKey, nil -} - -func decodeCertBag(asn1Data []byte) (x509Certificates []byte, err error) { - bag := new(certBag) - if err := unmarshal(asn1Data, bag); err != nil { - return nil, errors.New("pkcs12: error decoding cert bag: " + err.Error()) - } - if !bag.Id.Equal(oidCertTypeX509Certificate) { - return nil, NotImplementedError("only X509 certificates are supported") - } - return bag.Data, nil -} diff --git a/builder/azure/pkcs12/safebags_test.go b/builder/azure/pkcs12/safebags_test.go deleted file mode 100644 index f21a83c68..000000000 --- a/builder/azure/pkcs12/safebags_test.go +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright 2016 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. -package pkcs12 - -import ( - "crypto/rand" - "crypto/rsa" - "encoding/asn1" - "testing" -) - -// Assert the default algorithm parameters are in the correct order, -// and default to the correct value. Defaults are based on OpenSSL. -// 1. IterationCount, defaults to 2,048 long. -// 2. Salt, is 8 bytes long. -func TestDefaultAlgorithmParametersPkcs8ShroudedKeyBag(t *testing.T) { - privateKey, err := rsa.GenerateKey(rand.Reader, 512) - if err != nil { - t.Fatalf("failed to generate a private key: %s", err) - } - - password := []byte("sesame") - bytes, err := encodePkcs8ShroudedKeyBag(privateKey, password) - if err != nil { - t.Fatalf("failed to encode PKCS#8 shrouded key bag: %s", err) - } - - var pkinfo encryptedPrivateKeyInfo - rest, err := asn1.Unmarshal(bytes, &pkinfo) - if err != nil { - t.Fatalf("failed to unmarshal encryptedPrivateKeyInfo %s", err) - } - - if len(rest) != 0 { - t.Fatalf("unexpected trailing bytes of len=%d, bytes=%x", len(rest), rest) - } - - var params pbeParams - rest, err = asn1.Unmarshal(pkinfo.Algorithm().Parameters.FullBytes, ¶ms) - if err != nil { - t.Fatalf("failed to unmarshal encryptedPrivateKeyInfo %s", err) - } - - if len(rest) != 0 { - t.Fatalf("unexpected trailing bytes of len=%d, bytes=%x", len(rest), rest) - } - - if params.Iterations != pbeIterationCount { - t.Errorf("expected iteration count to be %d, but actual=%d", pbeIterationCount, params.Iterations) - } - if len(params.Salt) != pbeSaltSizeBytes { - t.Errorf("expected the number of salt bytes to be %d, but actual=%d", pbeSaltSizeBytes, len(params.Salt)) - } -} - -func TestRoundTripPkcs8ShroudedKeyBag(t *testing.T) { - privateKey, err := rsa.GenerateKey(rand.Reader, 512) - if err != nil { - t.Fatalf("failed to generate a private key: %s", err) - } - - password := []byte("sesame") - bytes, err := encodePkcs8ShroudedKeyBag(privateKey, password) - if err != nil { - t.Fatalf("failed to encode PKCS#8 shrouded key bag: %s", err) - } - - key, err := decodePkcs8ShroudedKeyBag(bytes, password) - if err != nil { - t.Fatalf("failed to decode PKCS#8 shrouded key bag: %s", err) - } - - actualPrivateKey := key.(*rsa.PrivateKey) - if actualPrivateKey.D.Cmp(privateKey.D) != 0 { - t.Fatalf("failed to round-trip rsa.PrivateKey.D") - } -} diff --git a/builder/azure/version/version.go b/builder/azure/version/version.go deleted file mode 100644 index 7e0d2c0f4..000000000 --- a/builder/azure/version/version.go +++ /dev/null @@ -1,13 +0,0 @@ -package version - -import ( - "github.com/hashicorp/packer-plugin-sdk/version" - packerVersion "github.com/hashicorp/packer/version" -) - -var AzurePluginVersion *version.PluginVersion - -func init() { - AzurePluginVersion = version.InitializePluginVersion( - packerVersion.Version, packerVersion.VersionPrerelease) -} diff --git a/command/plugin.go b/command/plugin.go index 5e007a0c2..58f718f84 100644 --- a/command/plugin.go +++ b/command/plugin.go @@ -13,9 +13,6 @@ import ( packersdk "github.com/hashicorp/packer-plugin-sdk/packer" "github.com/hashicorp/packer-plugin-sdk/plugin" - azurearmbuilder "github.com/hashicorp/packer/builder/azure/arm" - azurechrootbuilder "github.com/hashicorp/packer/builder/azure/chroot" - azuredtlbuilder "github.com/hashicorp/packer/builder/azure/dtl" filebuilder "github.com/hashicorp/packer/builder/file" nullbuilder "github.com/hashicorp/packer/builder/null" oneandonebuilder "github.com/hashicorp/packer/builder/oneandone" @@ -25,7 +22,6 @@ import ( compresspostprocessor "github.com/hashicorp/packer/post-processor/compress" manifestpostprocessor "github.com/hashicorp/packer/post-processor/manifest" shelllocalpostprocessor "github.com/hashicorp/packer/post-processor/shell-local" - azuredtlartifactprovisioner "github.com/hashicorp/packer/provisioner/azure-dtlartifact" breakpointprovisioner "github.com/hashicorp/packer/provisioner/breakpoint" fileprovisioner "github.com/hashicorp/packer/provisioner/file" inspecprovisioner "github.com/hashicorp/packer/provisioner/inspec" @@ -43,9 +39,6 @@ type PluginCommand struct { } var Builders = map[string]packersdk.Builder{ - "azure-arm": new(azurearmbuilder.Builder), - "azure-chroot": new(azurechrootbuilder.Builder), - "azure-dtl": new(azuredtlbuilder.Builder), "file": new(filebuilder.Builder), "null": new(nullbuilder.Builder), "oneandone": new(oneandonebuilder.Builder), @@ -53,17 +46,16 @@ var Builders = map[string]packersdk.Builder{ } var Provisioners = map[string]packersdk.Provisioner{ - "azure-dtlartifact": new(azuredtlartifactprovisioner.Provisioner), - "breakpoint": new(breakpointprovisioner.Provisioner), - "file": new(fileprovisioner.Provisioner), - "inspec": new(inspecprovisioner.Provisioner), - "powershell": new(powershellprovisioner.Provisioner), - "salt-masterless": new(saltmasterlessprovisioner.Provisioner), - "shell": new(shellprovisioner.Provisioner), - "shell-local": new(shelllocalprovisioner.Provisioner), - "sleep": new(sleepprovisioner.Provisioner), - "windows-restart": new(windowsrestartprovisioner.Provisioner), - "windows-shell": new(windowsshellprovisioner.Provisioner), + "breakpoint": new(breakpointprovisioner.Provisioner), + "file": new(fileprovisioner.Provisioner), + "inspec": new(inspecprovisioner.Provisioner), + "powershell": new(powershellprovisioner.Provisioner), + "salt-masterless": new(saltmasterlessprovisioner.Provisioner), + "shell": new(shellprovisioner.Provisioner), + "shell-local": new(shelllocalprovisioner.Provisioner), + "sleep": new(sleepprovisioner.Provisioner), + "windows-restart": new(windowsrestartprovisioner.Provisioner), + "windows-shell": new(windowsshellprovisioner.Provisioner), } var PostProcessors = map[string]packersdk.PostProcessor{ diff --git a/command/vendored_plugins.go b/command/vendored_plugins.go index b2ec67897..146b4452b 100644 --- a/command/vendored_plugins.go +++ b/command/vendored_plugins.go @@ -19,6 +19,10 @@ import ( anazibimportpostprocessor "github.com/hashicorp/packer-plugin-amazon/post-processor/import" ansibleprovisioner "github.com/hashicorp/packer-plugin-ansible/provisioner/ansible" ansiblelocalprovisioner "github.com/hashicorp/packer-plugin-ansible/provisioner/ansible-local" + azurearmbuilder "github.com/hashicorp/packer-plugin-azure/builder/azure/arm" + azurechrootbuilder "github.com/hashicorp/packer-plugin-azure/builder/azure/chroot" + azuredtlbuilder "github.com/hashicorp/packer-plugin-azure/builder/azure/dtl" + azuredtlartifactprovisioner "github.com/hashicorp/packer-plugin-azure/provisioner/azure-dtlartifact" chefclientprovisioner "github.com/hashicorp/packer-plugin-chef/provisioner/chef-client" chefsoloprovisioner "github.com/hashicorp/packer-plugin-chef/provisioner/chef-solo" cloudstackbuilder "github.com/hashicorp/packer-plugin-cloudstack/builder/cloudstack" @@ -94,6 +98,9 @@ var VendoredBuilders = map[string]packersdk.Builder{ "amazon-ebssurrogate": new(amazonebssurrogatebuilder.Builder), "amazon-ebsvolume": new(amazonebsvolumebuilder.Builder), "amazon-instance": new(amazoninstancebuilder.Builder), + "azure-arm": new(azurearmbuilder.Builder), + "azure-chroot": new(azurechrootbuilder.Builder), + "azure-dtl": new(azuredtlbuilder.Builder), "cloudstack": new(cloudstackbuilder.Builder), "digitalocean": new(digitaloceanbuilder.Builder), "docker": new(dockerbuilder.Builder), @@ -138,6 +145,7 @@ var VendoredBuilders = map[string]packersdk.Builder{ // VendoredProvisioners are provisioner components that were once bundled with the // Packer core, but are now being imported from their counterpart plugin repos var VendoredProvisioners = map[string]packersdk.Provisioner{ + "azure-dtlartifact": new(azuredtlartifactprovisioner.Provisioner), "ansible": new(ansibleprovisioner.Provisioner), "ansible-local": new(ansiblelocalprovisioner.Provisioner), "chef-client": new(chefclientprovisioner.Provisioner), diff --git a/contrib/azure-setup.sh b/contrib/azure-setup.sh deleted file mode 100755 index 7b7eac7c6..000000000 --- a/contrib/azure-setup.sh +++ /dev/null @@ -1,268 +0,0 @@ -#!/usr/bin/env bash -set -e - -meta_name= -azure_client_id= # Derived from application after creation -azure_client_name= # Application name -azure_client_secret= # Application password -azure_group_name= -azure_storage_name= -azure_subscription_id= # Derived from the account after login -azure_tenant_id= # Derived from the account after login -location= -azure_object_id= -azureversion= -create_sleep=10 - -showhelp() { - echo "azure-setup" - echo "" - echo " azure-setup helps you generate packer credentials for azure" - echo "" - echo " The script creates a resource group, storage account, application" - echo " (client), service principal, and permissions and displays a snippet" - echo " for use in your packer templates." - echo "" - echo " For simplicity we make a lot of assumptions and choose reasonable" - echo " defaults. If you want more control over what happens, please use" - echo " the azure-cli directly." - echo "" - echo " Note that you must already have an Azure account, username," - echo " password, and subscription. You can create those here:" - echo "" - echo " - https://azure.microsoft.com/en-us/account/" - echo "" - echo "REQUIREMENTS" - echo "" - echo " - azure-cli" - echo " - jq" - echo "" - echo " Use the requirements command (below) for more info." - echo "" - echo "USAGE" - echo "" - echo " ./azure-setup.sh requirements" - echo " ./azure-setup.sh setup" - echo "" -} - -requirements() { - found=0 - - azureversion=$(az --version) - if [ $? -eq 0 ]; then - found=$((found + 1)) - echo "Found azure-cli version: $azureversion" - else - echo "azure-cli is missing. Please install azure-cli from" - echo "https://docs.microsoft.com/en-us/cli/azure/install-azure-cli?view=azure-cli-latest" - echo "Alternatively, you can use the Cloud Shell https://docs.microsoft.com/en-us/azure/cloud-shell/overview right from the Azure Portal or even VS Code." - fi - - jqversion=$(jq --version) - if [ $? -eq 0 ]; then - found=$((found + 1)) - echo "Found jq version: $jqversion" - else - echo "jq is missing. Please install jq from" - echo "https://stedolan.github.io/jq/" - fi - - if [ $found -lt 2 ]; then - exit 1 - fi -} - -askSubscription() { - az account list -otable - echo "" - echo "Please enter the Id of the account you wish to use. If you do not see" - echo "a valid account in the list press Ctrl+C to abort and create one." - echo "If you leave this blank we will use the Current account." - echo -n "> " - read azure_subscription_id - - if [ "$azure_subscription_id" != "" ]; then - az account set --subscription $azure_subscription_id - else - azure_subscription_id=$(az account list --output json | jq -r '.[] | select(.isDefault==true) | .id') - fi - azure_tenant_id=$(az account list --output json | jq -r '.[] | select(.id=="'$azure_subscription_id'") | .tenantId') - echo "Using subscription_id: $azure_subscription_id" - echo "Using tenant_id: $azure_tenant_id" -} - -askName() { - echo "" - echo "Choose a name for your resource group, storage account and client" - echo "client. This is arbitrary, but it must not already be in use by" - echo "any of those resources. ALPHANUMERIC ONLY. Ex: mypackerbuild" - echo -n "> " - read meta_name -} - -askSecret() { - echo "" - echo "Enter a secret for your application. We recommend generating one with" - echo "openssl rand -base64 24. If you leave this blank we will attempt to" - echo "generate one for you using openssl. THIS WILL BE SHOWN IN PLAINTEXT." - echo "Ex: mypackersecret8734" - echo -n "> " - read azure_client_secret - if [ "$azure_client_secret" = "" ]; then - azure_client_secret=$(openssl rand -base64 24) - if [ $? -ne 0 ]; then - echo "Error generating secret" - exit 1 - fi - echo "Generated client_secret: $azure_client_secret" - fi -} - -askLocation() { - az account list-locations -otable - echo "" - echo "Choose which region your resource group and storage account will be created. example: westus" - echo -n "> " - read location -} - -createResourceGroup() { - echo "==> Creating resource group" - az group create -n $meta_name -l $location - if [ $? -eq 0 ]; then - azure_group_name=$meta_name - else - echo "Error creating resource group: $meta_name" - return 1 - fi -} - -createStorageAccount() { - echo "==> Creating storage account" - az storage account create --name $meta_name --resource-group $meta_name --location $location --kind Storage --sku Standard_LRS - if [ $? -eq 0 ]; then - azure_storage_name=$meta_name - else - echo "Error creating storage account: $meta_name" - return 1 - fi -} - -createApplication() { - echo "==> Creating application" - echo "==> Does application exist?" - azure_client_id=$(az ad app list --output json | jq -r '.[] | select(.displayName | contains("'$meta_name'")) ') - - if [ "$azure_client_id" != "" ]; then - echo "==> application already exist, grab appId" - azure_client_id=$(az ad app list --output json | jq -r '.[] | select(.displayName | contains("'$meta_name'")) .appId') - else - echo "==> application does not exist" - azure_client_id=$(az ad app create --display-name $meta_name --identifier-uris http://$meta_name --homepage http://$meta_name --password $azure_client_secret --output json | jq -r .appId) - fi - - if [ $? -ne 0 ]; then - echo "Error creating application: $meta_name @ http://$meta_name" - return 1 - fi -} - -createServicePrincipal() { - echo "==> Creating service principal" - azure_object_id=$(az ad sp create --id $azure_client_id --output json | jq -r .objectId) - echo $azure_object_id "was selected." - - if [ $? -ne 0 ]; then - echo "Error creating service principal: $azure_client_id" - return 1 - fi -} - -createPermissions() { - echo "==> Creating permissions" - az role assignment create --assignee $azure_object_id --role "Owner" --scope /subscriptions/$azure_subscription_id - # If the user wants to use a more conservative scope, she can. She must - # configure the Azure builder to use build_resource_group_name. The - # easiest solution is subscription wide permission. - # az role assignment create --spn http://$meta_name -g $azure_group_name -o "API Management Service Contributor" - if [ $? -ne 0 ]; then - echo "Error creating permissions for: http://$meta_name" - return 1 - fi -} - -showConfigs() { - echo "" - echo "Use the following configuration for your packer template:" - echo "" - echo "{" - echo " \"client_id\": \"$azure_client_id\"," - echo " \"client_secret\": \"$azure_client_secret\"," - echo " \"object_id\": \"$azure_object_id\"," - echo " \"subscription_id\": \"$azure_subscription_id\"," - echo " \"tenant_id\": \"$azure_tenant_id\"," - echo " \"resource_group_name\": \"$azure_group_name\"," - echo " \"storage_account\": \"$azure_storage_name\"," - echo "}" - echo "" -} - -doSleep() { - local sleep_time=${PACKER_SLEEP_TIME-$create_sleep} - echo "" - echo "Sleeping for ${sleep_time} seconds to wait for resources to be " - echo "created. If you get an error about a resource not existing, you can " - echo "try increasing the amount of time we wait after creating resources " - echo "by setting PACKER_SLEEP_TIME to something higher than the default." - echo "" - sleep $sleep_time -} - -retryable() { - n=0 - until [ $n -ge $1 ] - do - $2 && return 0 - echo "$2 failed. Retrying..." - n=$[$n+1] - doSleep - done - echo "$2 failed after $1 tries. Exiting." - exit 1 -} - - -setup() { - requirements - - az login - - askSubscription - askName - askSecret - askLocation - - # Some of the resources take a while to converge in the API. To make the - # script more reliable we'll add a sleep after we create each resource. - - retryable 3 createResourceGroup - retryable 3 createStorageAccount - retryable 3 createApplication - retryable 3 createServicePrincipal - retryable 3 createPermissions - - showConfigs -} - -case "$1" in - requirements) - requirements - ;; - setup) - setup - ;; - *) - showhelp - ;; -esac diff --git a/go.mod b/go.mod index f0d964e8e..38d0955de 100644 --- a/go.mod +++ b/go.mod @@ -2,18 +2,9 @@ module github.com/hashicorp/packer require ( github.com/1and1/oneandone-cloudserver-sdk-go v1.0.1 - github.com/Azure/azure-sdk-for-go v40.5.0+incompatible - github.com/Azure/go-autorest/autorest v0.10.0 - github.com/Azure/go-autorest/autorest/adal v0.8.2 - github.com/Azure/go-autorest/autorest/azure/auth v0.4.2 - github.com/Azure/go-autorest/autorest/azure/cli v0.3.1 - github.com/Azure/go-autorest/autorest/date v0.2.0 - github.com/Azure/go-autorest/autorest/to v0.3.0 - github.com/approvals/go-approval-tests v0.0.0-20160714161514-ad96e53bea43 github.com/biogo/hts v0.0.0-20160420073057-50da7d4131a3 github.com/cheggaaa/pb v1.0.27 github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e - github.com/dgrijalva/jwt-go v3.2.0+incompatible github.com/dsnet/compress v0.0.1 github.com/exoscale/packer-plugin-exoscale v0.1.1 github.com/gobwas/glob v0.2.3 @@ -22,15 +13,16 @@ require ( github.com/hako/durafmt v0.0.0-20200710122514-c0fb7b4da026 github.com/hashicorp/go-checkpoint v0.0.0-20171009173528-1545e56e46de github.com/hashicorp/go-cty-funcs v0.0.0-20200930094925-2721b1e36840 - github.com/hashicorp/go-getter/v2 v2.0.0-20200604122502-a6995fa1edad + github.com/hashicorp/go-getter/v2 v2.0.0 github.com/hashicorp/go-multierror v1.1.1 github.com/hashicorp/go-uuid v1.0.2 github.com/hashicorp/go-version v1.3.0 github.com/hashicorp/hcl/v2 v2.10.0 github.com/hashicorp/packer-plugin-alicloud v0.0.2 github.com/hashicorp/packer-plugin-amazon v0.0.1 - github.com/hashicorp/packer-plugin-ansible v0.0.2 - github.com/hashicorp/packer-plugin-chef v0.0.1 + github.com/hashicorp/packer-plugin-ansible v0.0.3 + github.com/hashicorp/packer-plugin-azure v0.0.2 + github.com/hashicorp/packer-plugin-chef v0.0.2 github.com/hashicorp/packer-plugin-cloudstack v0.0.1 github.com/hashicorp/packer-plugin-converge v0.0.1 github.com/hashicorp/packer-plugin-digitalocean v0.0.1 @@ -49,10 +41,10 @@ require ( github.com/hashicorp/packer-plugin-outscale v0.0.1 github.com/hashicorp/packer-plugin-parallels v0.0.1 github.com/hashicorp/packer-plugin-proxmox v0.0.2 - github.com/hashicorp/packer-plugin-puppet v0.0.1 + github.com/hashicorp/packer-plugin-puppet v0.0.2 github.com/hashicorp/packer-plugin-qemu v0.0.1 github.com/hashicorp/packer-plugin-scaleway v0.0.1 - github.com/hashicorp/packer-plugin-sdk v0.2.0 + github.com/hashicorp/packer-plugin-sdk v0.2.1 github.com/hashicorp/packer-plugin-tencentcloud v0.0.1 github.com/hashicorp/packer-plugin-triton v0.0.0-20210421085122-768dd7c764d9 github.com/hashicorp/packer-plugin-ucloud v0.0.1 @@ -69,14 +61,13 @@ require ( github.com/mitchellh/mapstructure v1.4.1 github.com/mitchellh/panicwrap v1.0.0 github.com/mitchellh/prefixedio v0.0.0-20151214002211-6e6954073784 - github.com/mitchellh/reflectwalk v1.0.0 github.com/pierrec/lz4 v2.0.5+incompatible github.com/posener/complete v1.2.3 github.com/profitbricks/profitbricks-sdk-go v4.0.2+incompatible github.com/shirou/gopsutil v3.21.1+incompatible github.com/stretchr/testify v1.7.0 - github.com/ulikunitz/xz v0.5.6 - github.com/zclconf/go-cty v1.8.2 + github.com/ulikunitz/xz v0.5.8 + github.com/zclconf/go-cty v1.8.3 github.com/zclconf/go-cty-yaml v1.0.1 golang.org/x/crypto v0.0.0-20210415154028-4f45737414dc golang.org/x/mod v0.4.1 diff --git a/go.sum b/go.sum index 5d34e64cf..39f323b5d 100644 --- a/go.sum +++ b/go.sum @@ -41,39 +41,49 @@ cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9 dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/1and1/oneandone-cloudserver-sdk-go v1.0.1 h1:RMTyvS5bjvSWiUcfqfr/E2pxHEMrALvU+E12n6biymg= github.com/1and1/oneandone-cloudserver-sdk-go v1.0.1/go.mod h1:61apmbkVJH4kg+38ftT+/l0XxdUCVnHggqcOTqZRSEE= -github.com/Azure/azure-sdk-for-go v40.5.0+incompatible h1:CVQNKuUepSFBo6BW6gM1J9slPHLRcjn6vaw+j+causw= github.com/Azure/azure-sdk-for-go v40.5.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= +github.com/Azure/azure-sdk-for-go v51.2.0+incompatible h1:qQNk//OOHK0GZcgMMgdJ4tZuuh0zcOeUkpTxjvKFpSQ= +github.com/Azure/azure-sdk-for-go v51.2.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI= github.com/Azure/go-autorest/autorest v0.9.3/go.mod h1:GsRuLYvwzLjjjRoWEIyMUaYq8GNUx2nRB378IPt/1p0= -github.com/Azure/go-autorest/autorest v0.10.0 h1:mvdtztBqcL8se7MdrUweNieTNi4kfNG6GOJuurQJpuY= github.com/Azure/go-autorest/autorest v0.10.0/go.mod h1:/FALq9T/kS7b5J5qsQ+RSTUdAmGFqi0vUdVNNx8q630= +github.com/Azure/go-autorest/autorest v0.11.18 h1:90Y4srNYrwOtAgVo3ndrQkTYn6kf1Eg/AjTFJ8Is2aM= +github.com/Azure/go-autorest/autorest v0.11.18/go.mod h1:dSiJPy22c3u0OtOKDNttNgqpNFY/GeWa7GH/Pz56QRA= github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0= github.com/Azure/go-autorest/autorest/adal v0.8.0/go.mod h1:Z6vX6WXXuyieHAXwMj0S6HY6e6wcHn37qQMBQlvY3lc= github.com/Azure/go-autorest/autorest/adal v0.8.1/go.mod h1:ZjhuQClTqx435SRJ2iMlOxPYt3d2C/T/7TiQCVZSn3Q= -github.com/Azure/go-autorest/autorest/adal v0.8.2 h1:O1X4oexUxnZCaEUGsvMnr8ZGj8HI37tNezwY4npRqA0= github.com/Azure/go-autorest/autorest/adal v0.8.2/go.mod h1:ZjhuQClTqx435SRJ2iMlOxPYt3d2C/T/7TiQCVZSn3Q= +github.com/Azure/go-autorest/autorest/adal v0.9.5/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A= +github.com/Azure/go-autorest/autorest/adal v0.9.13 h1:Mp5hbtOePIzM8pJVRa3YLrWWmZtoxRXqUEzCfJt3+/Q= +github.com/Azure/go-autorest/autorest/adal v0.9.13/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M= github.com/Azure/go-autorest/autorest/azure/auth v0.4.2 h1:iM6UAvjR97ZIeR93qTcwpKNMpV+/FTWjwEbuPD495Tk= github.com/Azure/go-autorest/autorest/azure/auth v0.4.2/go.mod h1:90gmfKdlmKgfjUpnCEpOJzsUEjrWDSLwHIG73tSXddM= -github.com/Azure/go-autorest/autorest/azure/cli v0.3.1 h1:LXl088ZQlP0SBppGFsRZonW6hSvwgL5gRByMbvUbx8U= github.com/Azure/go-autorest/autorest/azure/cli v0.3.1/go.mod h1:ZG5p860J94/0kI9mNJVoIoLgXcirM2gF5i2kWloofxw= +github.com/Azure/go-autorest/autorest/azure/cli v0.4.2 h1:dMOmEJfkLKW/7JsokJqkyoYSgmR08hi9KrhjZb+JALY= +github.com/Azure/go-autorest/autorest/azure/cli v0.4.2/go.mod h1:7qkJkT+j6b+hIpzMOwPChJhTqS8VbsqqgULzMNRugoM= github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA= -github.com/Azure/go-autorest/autorest/date v0.2.0 h1:yW+Zlqf26583pE43KhfnhFcdmSWlm5Ew6bxipnr/tbM= github.com/Azure/go-autorest/autorest/date v0.2.0/go.mod h1:vcORJHLJEh643/Ioh9+vPmf1Ij9AEBM5FuBIXLmIy0g= +github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw= +github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= -github.com/Azure/go-autorest/autorest/mocks v0.3.0 h1:qJumjCaCudz+OcqE9/XtEPfvtOjOmKaui4EOpFI6zZc= github.com/Azure/go-autorest/autorest/mocks v0.3.0/go.mod h1:a8FDP3DYzQ4RYfVAxAN3SVSiiO77gL2j2ronKKP0syM= -github.com/Azure/go-autorest/autorest/to v0.3.0 h1:zebkZaadz7+wIQYgC7GXaz3Wb28yKYfVkkBKwc38VF8= +github.com/Azure/go-autorest/autorest/mocks v0.4.1 h1:K0laFcLE6VLTOwNgSxaGbUcLPuGXlNkbVvq4cW4nIHk= +github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= github.com/Azure/go-autorest/autorest/to v0.3.0/go.mod h1:MgwOyqaIuKdG4TL/2ywSsIWKAfJfgHDo8ObuUk3t5sA= +github.com/Azure/go-autorest/autorest/to v0.4.0 h1:oXVqrxakqqV1UZdSazDOPOLvOIz+XA683u8EctwboHk= +github.com/Azure/go-autorest/autorest/to v0.4.0/go.mod h1:fE8iZBn7LQR7zH/9XU2NcPR4o9jEImooCeWJcYV/zLE= github.com/Azure/go-autorest/autorest/validation v0.2.0/go.mod h1:3EEqHnBxQGHXRYq3HT1WyXAvT7LLY3tl70hw6tQIbjI= github.com/Azure/go-autorest/autorest/validation v0.3.1 h1:AgyqjAd94fwNAoTjl/WQXg4VvFeRFpO+UhNyRXqF1ac= github.com/Azure/go-autorest/autorest/validation v0.3.1/go.mod h1:yhLgjC0Wda5DYXl6JAsWyUe4KVNffhoDhG0zVzUMo3E= -github.com/Azure/go-autorest/logger v0.1.0 h1:ruG4BSDXONFRrZZJ2GUXDiUyVpayPmb1GnWeHDdaNKY= github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc= -github.com/Azure/go-autorest/tracing v0.5.0 h1:TRn4WjSnkcSy5AEG3pnbtFSwNtwzjr4VYyQflFE619k= +github.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+ZtXWSmf4Tg= +github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk= +github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo= +github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= github.com/Azure/go-ntlmssp v0.0.0-20180810175552-4a21cbd618b4/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU= github.com/Azure/go-ntlmssp v0.0.0-20191115201650-bad6df29494a h1:3FwiePtHk5YJrooV799oo5jIfsgRdES25VdngJM03dU= github.com/Azure/go-ntlmssp v0.0.0-20191115201650-bad6df29494a/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU= @@ -126,8 +136,9 @@ github.com/apparentlymart/go-textseg v1.0.0/go.mod h1:z96Txxhf3xSFMPmb5X/1W05FF/ github.com/apparentlymart/go-textseg/v12 v12.0.0/go.mod h1:S/4uRK2UtaQttw1GenVJEynmyUenKwP++x/+DdGV/Ec= github.com/apparentlymart/go-textseg/v13 v13.0.0 h1:Y+KvPE1NYz0xl601PVImeQfFyEy6iT90AvPUL1NNfNw= github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo= -github.com/approvals/go-approval-tests v0.0.0-20160714161514-ad96e53bea43 h1:ePCAQPf5tUc5IMcUvu6euhSGna7jzs7eiXtJXHig6Zc= github.com/approvals/go-approval-tests v0.0.0-20160714161514-ad96e53bea43/go.mod h1:S6puKjZ9ZeqUPBv2hEBnMZGcM2J6mOsDRQcmxkMAND0= +github.com/approvals/go-approval-tests v0.0.0-20210131072903-38d0b0ec12b1 h1:uroQ0JaeVom9Ffv9xFtc7DcqrpGmyQeZCRzHD9FqPBg= +github.com/approvals/go-approval-tests v0.0.0-20210131072903-38d0b0ec12b1/go.mod h1:PJOqSY8IofNv3heAD6k8E7EfFS6okiSS9bSAasaAUME= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= @@ -231,10 +242,12 @@ github.com/digitalocean/go-qemu v0.0.0-20210326154740-ac9e0b687001/go.mod h1:Iet github.com/digitalocean/godo v1.11.1/go.mod h1:h6faOIcZ8lWIwNQ+DN7b3CgX4Kwby5T+nbpNqkUIozU= github.com/digitalocean/godo v1.60.0 h1:o/vimtn/HKtYSakFAAZ59Zc5ASORd41S4z1X7pAXPn8= github.com/digitalocean/godo v1.60.0/go.mod h1:p7dOjjtSBqCTUksqtA5Fd3uaKs9kyTq2xcz76ulEJRU= -github.com/dimchansky/utfbom v1.1.0 h1:FcM3g+nofKgUteL8dm/UpdRXNC9KmADgTpLKsu0TRo4= github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8= -github.com/dnaeon/go-vcr v1.0.1 h1:r8L/HqC0Hje5AXMu1ooW8oyQyOFv4GxqpL0nRP7SLLY= +github.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi/U= +github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE= github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= +github.com/dnaeon/go-vcr v1.1.0 h1:ReYa/UBrRyQdant9B4fNHGoCNKw6qh6P0fsdGmZpR7c= +github.com/dnaeon/go-vcr v1.1.0/go.mod h1:M7tiix8f0r6mKKJ3Yq/kqU1OYf3MnfmBWVbPx/yU9ko= github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= github.com/dsnet/compress v0.0.1 h1:PlZu0n3Tuv04TzpfPbrnI0HW/YwodEXDS+oPKahKF0Q= github.com/dsnet/compress v0.0.1/go.mod h1:Aw8dCMJ7RioblQeTqt88akK31OvO8Dhf5JflhBbQEHo= @@ -266,6 +279,8 @@ github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/fatih/structtag v1.0.0/go.mod h1:IKitwq45uXL/yqi5mYghiD3w9H6eTOvI9vnk8tXMphA= github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94= +github.com/form3tech-oss/jwt-go v3.2.2+incompatible h1:TcekIExNqud5crz4xD2pavyTgWiPvpYe4Xau31I0PRk= +github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/getkin/kin-openapi v0.13.0/go.mod h1:WGRs2ZMM1Q8LR1QBEwUxC6RJEfaBcD0s+pcEVXFuAjw= github.com/getkin/kin-openapi v0.37.0/go.mod h1:ZJSfy1PxJv2QQvH9EdBj3nupRTVvV42mkW6zKUlRBwk= @@ -437,6 +452,8 @@ github.com/hashicorp/consul/sdk v0.4.0 h1:zBtCfKJZcJDBvSCkQJch4ulp59m1rATFLKwNo/ github.com/hashicorp/consul/sdk v0.4.0/go.mod h1:fY08Y9z5SvJqevyZNy6WWPXiG3KwBPAvlcdx16zZ0fM= github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-azure-helpers v0.16.0 h1:f8LqrmC7K545DSk9EvymtuiFZ8tlTC7+XkvmAZmUeiM= +github.com/hashicorp/go-azure-helpers v0.16.0/go.mod h1:kR7+sTDEb9TOp/O80ss1UEJg1t4/BHLD/U8wHLS4BGQ= github.com/hashicorp/go-checkpoint v0.0.0-20171009173528-1545e56e46de h1:XDCSythtg8aWSRSO29uwhgh7b127fWr+m5SemqjSUL8= github.com/hashicorp/go-checkpoint v0.0.0-20171009173528-1545e56e46de/go.mod h1:xIwEieBHERyEvaeKF/TcHh1Hu+lxPM+n2vT1+g9I4m4= github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= @@ -452,8 +469,9 @@ github.com/hashicorp/go-getter/gcs/v2 v2.0.0-20200604122502-a6995fa1edad/go.mod github.com/hashicorp/go-getter/s3/v2 v2.0.0-20200604122502-a6995fa1edad h1:hwk5mQRwVvZc/f+So1kHbOhW/f7P2fEcCr2D5pSk9sI= github.com/hashicorp/go-getter/s3/v2 v2.0.0-20200604122502-a6995fa1edad/go.mod h1:kcB6Mv+0wzYXbQjTAeD/Pb85145WcFk2EElpe02fuoE= github.com/hashicorp/go-getter/v2 v2.0.0-20200511090339-3107ec4af37a/go.mod h1:QJ+LwRM91JBKBLyHoKBrcmi49i9Tu/ItpgTNXWSnpGM= -github.com/hashicorp/go-getter/v2 v2.0.0-20200604122502-a6995fa1edad h1:BfRfKjQgvwJrXF2apbeqoqeuxzDcxZIeI/eCj6HLkM4= github.com/hashicorp/go-getter/v2 v2.0.0-20200604122502-a6995fa1edad/go.mod h1:bM2M11foprILIk1Slmr2wBrJYsdQTym//+QMJqgm3F8= +github.com/hashicorp/go-getter/v2 v2.0.0 h1:wamdcQazMBZK6VwUo3HAOWLkcOJBWBoXPKfmf7/S17w= +github.com/hashicorp/go-getter/v2 v2.0.0/go.mod h1:w65fE5glbccYjndAuj1kA5lnVBGZYEaH0e5qA1kpIks= github.com/hashicorp/go-hclog v0.0.0-20180709165350-ff2cf002a8dd/go.mod h1:9bjs9uLqI8l75knNv3lV1kA55veR+WUPSiKIWcQHudI= github.com/hashicorp/go-hclog v0.8.0/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= github.com/hashicorp/go-hclog v0.12.0 h1:d4QkX8FRTYaKaCZBoXYY8zJX2BXjWxurN/GA2tkrmZM= @@ -491,6 +509,7 @@ github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/b github.com/hashicorp/go-version v1.0.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go-version v1.3.0 h1:McDWVJIU/y+u1BRV06dPaLfLCaT7fUTJLp5r04x7iNw= github.com/hashicorp/go-version v1.3.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= @@ -526,10 +545,12 @@ github.com/hashicorp/packer-plugin-alicloud v0.0.2 h1:uBVp53+yOfzbhUjC8WtQ/7uLcf github.com/hashicorp/packer-plugin-alicloud v0.0.2/go.mod h1:RCU4CLSJwSqHoNLlA+UghRw1JXqqzCPOE6Pv/EYjtgU= github.com/hashicorp/packer-plugin-amazon v0.0.1 h1:EuyjNK9bL7WhQeIJzhBJxOx8nyc61ai5UbOsb1PIVwI= github.com/hashicorp/packer-plugin-amazon v0.0.1/go.mod h1:12c9msibyHdId+Mk/pCbdRb1KaLIhaNyxeJ6n8bZt30= -github.com/hashicorp/packer-plugin-ansible v0.0.2 h1:nvBtCedXhUI5T6Up5+bmhlY7rmk8FjWuFv9A2joK7TU= -github.com/hashicorp/packer-plugin-ansible v0.0.2/go.mod h1:ocXB4KTU+I+DBRGfMP4XE7dPlURaUnb7NJvyddZ6bh0= -github.com/hashicorp/packer-plugin-chef v0.0.1 h1:1zQwnnvftwg9PJyWjMfHfDyzfWDdb0eo9IX8fX6kd+Y= -github.com/hashicorp/packer-plugin-chef v0.0.1/go.mod h1:4iSyWfvrb4QwUDZqJ3iCb+kIsnDwOTL1yTEDXBtk3Ew= +github.com/hashicorp/packer-plugin-ansible v0.0.3 h1:pLL2ZqRt4LVBwhtcG/PVgr9WbhfYfIDJ2aWT+Q7ef9U= +github.com/hashicorp/packer-plugin-ansible v0.0.3/go.mod h1:5/wOgs7TBwziYCznulfv5AwncLHavXQr83EtpkBVlXg= +github.com/hashicorp/packer-plugin-azure v0.0.2 h1:wNEWpkIUzFr/K0ddlipn7W7oJ/m8+RiWZ1xJMsX+hbM= +github.com/hashicorp/packer-plugin-azure v0.0.2/go.mod h1:ySskXX3DJV9Z9Yzt3dyrWsN1XUcjeIOtyL7/ZNHs6zw= +github.com/hashicorp/packer-plugin-chef v0.0.2 h1:JiciRcYGHaHB0LoJ0Y4oSJXrZeH0xbnshcEYGqC3lgI= +github.com/hashicorp/packer-plugin-chef v0.0.2/go.mod h1:PxGw+J6PTW74b8MzMDEIoVYHAIr+vCS1n0etz8pqdiM= github.com/hashicorp/packer-plugin-cloudstack v0.0.1 h1:BF9nXRlA0xQV5W/+CoLjWn0aLO60gTbsxnLi/o37ktc= github.com/hashicorp/packer-plugin-cloudstack v0.0.1/go.mod h1:fx13TY2szz6cm2e99xzU3gQzKdGVwysxY2TyKr0r8MQ= github.com/hashicorp/packer-plugin-converge v0.0.1 h1:cjrNt2Q/BuSH2o2bpNV91DhWYSTN7vb4LwxwFXULcok= @@ -566,8 +587,8 @@ github.com/hashicorp/packer-plugin-parallels v0.0.1 h1:fcaaiGWdU1+X4IGadXdUhJ2si github.com/hashicorp/packer-plugin-parallels v0.0.1/go.mod h1:FGNtZ7XFBr3IYuj7uvJtSaNnyhAwe457zP464m06+20= github.com/hashicorp/packer-plugin-proxmox v0.0.2 h1:x6QW7PeKh+IJymPEt3QdpBhSRi5vqXb8qTWv7rMLuns= github.com/hashicorp/packer-plugin-proxmox v0.0.2/go.mod h1:3URutEWX1yy10qcHNJncS4OMpZknA1FyvlrfL+5usYk= -github.com/hashicorp/packer-plugin-puppet v0.0.1 h1:avOlJB2suArgqo3gn/JipLtHY0TVuTcweYA3GxVceyY= -github.com/hashicorp/packer-plugin-puppet v0.0.1/go.mod h1:HgRpw6hXYykjyceBRbrRvBT7vSU7Ofo5HIfFRMCehQc= +github.com/hashicorp/packer-plugin-puppet v0.0.2 h1:ER0znnTwRAlWqnTg3JLo//Cgj3wy+LjTVzrzcYFbT7k= +github.com/hashicorp/packer-plugin-puppet v0.0.2/go.mod h1:3KBIWHblXAoGZSl5QRjW1df4vOF0q2XOkHDjky8/fmA= github.com/hashicorp/packer-plugin-qemu v0.0.1 h1:yGnmWf4Z+ZmOJXJF6w23V2KChtTCiPHsFnfg7+LRu74= github.com/hashicorp/packer-plugin-qemu v0.0.1/go.mod h1:8Q/LCjO7oplLcLe1KLdEt7rq94h42Di6Lab2DTLNwVg= github.com/hashicorp/packer-plugin-scaleway v0.0.1 h1:FSq76WuMJNKwCvF77v06EpuLe/AJP3sG5/YrfL41kcw= @@ -586,8 +607,9 @@ github.com/hashicorp/packer-plugin-sdk v0.1.2/go.mod h1:KRjczE1/c9NV5Re+PXt3myJs github.com/hashicorp/packer-plugin-sdk v0.1.3-0.20210407232143-c217d82aefb6/go.mod h1:xePpgQgQYv/bamiypx3hH9ukidxDdcN8q0R0wLi8IEQ= github.com/hashicorp/packer-plugin-sdk v0.1.3/go.mod h1:xePpgQgQYv/bamiypx3hH9ukidxDdcN8q0R0wLi8IEQ= github.com/hashicorp/packer-plugin-sdk v0.1.4/go.mod h1:xePpgQgQYv/bamiypx3hH9ukidxDdcN8q0R0wLi8IEQ= -github.com/hashicorp/packer-plugin-sdk v0.2.0 h1:A4Dq7p4y1vscY4gMzp7GQaXyDJYYhP4ukp4fapPSOY4= github.com/hashicorp/packer-plugin-sdk v0.2.0/go.mod h1:0DiOMEBldmB0HEhp0npFSSygC8bIvW43pphEgWkp2WU= +github.com/hashicorp/packer-plugin-sdk v0.2.1 h1:NZJ9h2ddzZb6E3eaYFD7L4mSjqFia3FDoDTxDGQKNMs= +github.com/hashicorp/packer-plugin-sdk v0.2.1/go.mod h1:4V7lS35FRhukvZrW41IPctTPY7JmHPOkFZcR7XGXZPk= github.com/hashicorp/packer-plugin-tencentcloud v0.0.1 h1:DR7GETCzrK/DPFMUPbULIklCxwGhstbbz6pl+2S+UnM= github.com/hashicorp/packer-plugin-tencentcloud v0.0.1/go.mod h1:FmdacMLvDKiT6OdMAc2x4LXtqu/soLApH3jF57SWOik= github.com/hashicorp/packer-plugin-triton v0.0.0-20210421085122-768dd7c764d9 h1:No5oPI9Wa7FhTKkFJwI3hcfUVvEpgPC8QMcG9l/Vxzo= @@ -764,14 +786,16 @@ github.com/mitchellh/panicwrap v1.0.0 h1:67zIyVakCIvcs69A0FGfZjBdPleaonSgGlXRSRl github.com/mitchellh/panicwrap v1.0.0/go.mod h1:pKvZHwWrZowLUzftuFq7coarnxbBXU4aQh3N0BJOeeA= github.com/mitchellh/prefixedio v0.0.0-20151214002211-6e6954073784 h1:+DAetXqxv/mSyCkE9KBIYOZs9b68y7SUaDCxQMRjA68= github.com/mitchellh/prefixedio v0.0.0-20151214002211-6e6954073784/go.mod h1:kB1naBgV9ORnkiTVeyJOI1DavaJkG4oNIq0Af6ZVKUo= -github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY= github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= +github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8= github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= @@ -913,8 +937,9 @@ github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljT github.com/ugorji/go/codec v1.2.4 h1:C5VurWRRCKjuENsbM6GYVw8W++WVW9rSxoACKIvxzz8= github.com/ugorji/go/codec v1.2.4/go.mod h1:bWBu1+kIRWcF8uMklKaJrR6fTWQOwAlrIzX22pHwryA= github.com/ulikunitz/xz v0.5.5/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8= -github.com/ulikunitz/xz v0.5.6 h1:jGHAfXawEGZQ3blwU5wnWKQJvAraT7Ftq9EXjnXYgt8= github.com/ulikunitz/xz v0.5.6/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8= +github.com/ulikunitz/xz v0.5.8 h1:ERv8V6GKqVi23rgu5cj9pVfVzJbOqAY2Ntl88O6c2nQ= +github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= github.com/valyala/fasttemplate v1.1.0/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= @@ -948,8 +973,9 @@ github.com/zclconf/go-cty v1.4.0/go.mod h1:nHzOclRkoj++EU9ZjSrZvRG0BXIWt8c7loYc0 github.com/zclconf/go-cty v1.7.0/go.mod h1:VDR4+I79ubFBGm1uJac1226K5yANQFHeauxPBoP54+o= github.com/zclconf/go-cty v1.8.0/go.mod h1:vVKLxnk3puL4qRAv72AO+W99LUD4da90g3uUAzyuvAk= github.com/zclconf/go-cty v1.8.1/go.mod h1:vVKLxnk3puL4qRAv72AO+W99LUD4da90g3uUAzyuvAk= -github.com/zclconf/go-cty v1.8.2 h1:u+xZfBKgpycDnTNjPhGiTEYZS5qS/Sb5MqSfm7vzcjg= github.com/zclconf/go-cty v1.8.2/go.mod h1:vVKLxnk3puL4qRAv72AO+W99LUD4da90g3uUAzyuvAk= +github.com/zclconf/go-cty v1.8.3 h1:48gwZXrdSADU2UW9eZKHprxAI7APZGW9XmExpJpSjT0= +github.com/zclconf/go-cty v1.8.3/go.mod h1:vVKLxnk3puL4qRAv72AO+W99LUD4da90g3uUAzyuvAk= github.com/zclconf/go-cty-debug v0.0.0-20191215020915-b22d67c1ba0b/go.mod h1:ZRKQfBXbGkpdV6QMzT3rU1kSTAnfu1dO8dPKjYprgj8= github.com/zclconf/go-cty-yaml v1.0.1 h1:up11wlgAaDvlAGENcFDnZgkn0qUJurso7k6EpURKNF8= github.com/zclconf/go-cty-yaml v1.0.1/go.mod h1:IP3Ylp0wQpYm50IHK8OZWKMu6sPJIUgKa8XhiVHura0= @@ -985,8 +1011,10 @@ golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20200422194213-44a606286825/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201208171446-5f87f3452ae9/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210415154028-4f45737414dc h1:+q90ECDSAQirdykUN6sPEiBXBsp8Csjcca8Oy7bgLTA= golang.org/x/crypto v0.0.0-20210415154028-4f45737414dc/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= diff --git a/provisioner/azure-dtlartifact/provisioner.go b/provisioner/azure-dtlartifact/provisioner.go deleted file mode 100644 index c48873427..000000000 --- a/provisioner/azure-dtlartifact/provisioner.go +++ /dev/null @@ -1,183 +0,0 @@ -//go:generate packer-sdc mapstructure-to-hcl2 -type Config,DtlArtifact,ArtifactParameter - -package devtestlabsartifacts - -import ( - "context" - "fmt" - "time" - - "github.com/Azure/azure-sdk-for-go/services/devtestlabs/mgmt/2018-09-15/dtl" - "github.com/hashicorp/hcl/v2/hcldec" - "github.com/hashicorp/packer/builder/azure/common/client" - dtlBuilder "github.com/hashicorp/packer/builder/azure/dtl" - - packersdk "github.com/hashicorp/packer-plugin-sdk/packer" - - "github.com/hashicorp/packer-plugin-sdk/common" - "github.com/hashicorp/packer-plugin-sdk/template/config" - "github.com/hashicorp/packer-plugin-sdk/template/interpolate" -) - -type DtlArtifact struct { - ArtifactName string `mapstructure:"artifact_name"` - ArtifactId string `mapstructure:"artifact_id"` - Parameters []ArtifactParameter `mapstructure:"parameters"` -} - -type ArtifactParameter struct { - Name string `mapstructure:"name"` - Value string `mapstructure:"value"` - Type string `mapstructure:"type"` -} - -type Config struct { - common.PackerConfig `mapstructure:",squash"` - - // Authentication via OAUTH - ClientConfig client.Config `mapstructure:",squash"` - - DtlArtifacts []DtlArtifact `mapstructure:"dtl_artifacts"` - LabName string `mapstructure:"lab_name"` - - ResourceGroupName string `mapstructure:"lab_resource_group_name"` - - VMName string `mapstructure:"vm_name"` - - // The default PollingDuration for azure is 15mins, this property will override - // that value. See [Azure DefaultPollingDuration](https://godoc.org/github.com/Azure/go-autorest/autorest#pkg-constants) - // If your Packer build is failing on the - // ARM deployment step with the error `Original Error: - // context deadline exceeded`, then you probably need to increase this timeout from - // its default of "15m" (valid time units include `s` for seconds, `m` for - // minutes, and `h` for hours.) - PollingDurationTimeout time.Duration `mapstructure:"polling_duration_timeout" required:"false"` - - AzureTags map[string]*string `mapstructure:"azure_tags"` - - Json map[string]interface{} - - ctx interpolate.Context -} - -type Provisioner struct { - config Config - communicator packersdk.Communicator -} - -func (p *Provisioner) ConfigSpec() hcldec.ObjectSpec { return p.config.FlatMapstructure().HCL2Spec() } - -func (p *Provisioner) Prepare(raws ...interface{}) error { - // // Create passthrough for winrm password so we can fill it in once we know - // // it - // p.config.ctx.Data = &EnvVarsTemplate{ - // WinRMPassword: `{{.WinRMPassword}}`, - // } - err := config.Decode(&p.config, &config.DecodeOpts{ - PluginType: "azure-dtlartifact", - Interpolate: true, - InterpolateContext: &p.config.ctx, - InterpolateFilter: &interpolate.RenderFilter{ - Exclude: []string{ - "execute_command", - }, - }, - }, raws...) - if err != nil { - return err - } - - p.config.ClientConfig.CloudEnvironmentName = "Public" - - return nil -} - -func (p *Provisioner) Communicator() packersdk.Communicator { - return p.communicator -} - -func (p *Provisioner) Provision(ctx context.Context, ui packersdk.Ui, comm packersdk.Communicator, _ map[string]interface{}) error { - - p.communicator = comm - - err := p.config.ClientConfig.SetDefaultValues() - if err != nil { - ui.Say(fmt.Sprintf("Error saving debug key: %s", err)) - return nil - } - - ///////////////////////////////////////////// - // Polling Duration Timeout - if p.config.PollingDurationTimeout == 0 { - // In the sdk, the default is 15 m. - p.config.PollingDurationTimeout = 15 * time.Minute - } - // FillParameters function captures authType and sets defaults. - err = p.config.ClientConfig.FillParameters() - if err != nil { - return err - } - - spnCloud, err := p.config.ClientConfig.GetServicePrincipalToken(ui.Say, p.config.ClientConfig.CloudEnvironment().ResourceManagerEndpoint) - if err != nil { - return err - } - - ui.Message("Creating Azure Resource Manager (ARM) client ...") - azureClient, err := dtlBuilder.NewAzureClient( - p.config.ClientConfig.SubscriptionID, - "", - p.config.ClientConfig.CloudEnvironment(), - p.config.PollingDurationTimeout, - p.config.PollingDurationTimeout, - spnCloud) - - if err != nil { - ui.Say(fmt.Sprintf("Error saving debug key: %s", err)) - return err - } - - ui.Say("Installing Artifact DTL") - dtlArtifacts := []dtl.ArtifactInstallProperties{} - - if p.config.DtlArtifacts != nil { - for i := range p.config.DtlArtifacts { - p.config.DtlArtifacts[i].ArtifactId = fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.DevTestLab/labs/%s/artifactSources/public repo/artifacts/%s", - p.config.ClientConfig.SubscriptionID, - p.config.ResourceGroupName, - p.config.LabName, - p.config.DtlArtifacts[i].ArtifactName) - - dparams := []dtl.ArtifactParameterProperties{} - for j := range p.config.DtlArtifacts[i].Parameters { - dp := &dtl.ArtifactParameterProperties{} - dp.Name = &p.config.DtlArtifacts[i].Parameters[j].Name - dp.Value = &p.config.DtlArtifacts[i].Parameters[j].Value - - dparams = append(dparams, *dp) - } - Aip := dtl.ArtifactInstallProperties{ - ArtifactID: &p.config.DtlArtifacts[i].ArtifactId, - Parameters: &dparams, - ArtifactTitle: &p.config.DtlArtifacts[i].ArtifactName, - } - dtlArtifacts = append(dtlArtifacts, Aip) - } - } - - dtlApplyArifactRequest := dtl.ApplyArtifactsRequest{ - Artifacts: &dtlArtifacts, - } - - ui.Say("Applying artifact ") - f, err := azureClient.DtlVirtualMachineClient.ApplyArtifacts(ctx, p.config.ResourceGroupName, p.config.LabName, p.config.VMName, dtlApplyArifactRequest) - - if err == nil { - err = f.WaitForCompletionRef(ctx, azureClient.DtlVirtualMachineClient.Client) - } - if err != nil { - ui.Say(fmt.Sprintf("Error Applying artifact: %s", err)) - } - ui.Say("Aftifact installed") - return err -} diff --git a/provisioner/azure-dtlartifact/provisioner.hcl2spec.go b/provisioner/azure-dtlartifact/provisioner.hcl2spec.go deleted file mode 100644 index e966230b6..000000000 --- a/provisioner/azure-dtlartifact/provisioner.hcl2spec.go +++ /dev/null @@ -1,133 +0,0 @@ -// Code generated by "packer-sdc mapstructure-to-hcl2"; DO NOT EDIT. - -package devtestlabsartifacts - -import ( - "github.com/hashicorp/hcl/v2/hcldec" - "github.com/zclconf/go-cty/cty" -) - -// FlatArtifactParameter is an auto-generated flat version of ArtifactParameter. -// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. -type FlatArtifactParameter struct { - Name *string `mapstructure:"name" cty:"name" hcl:"name"` - Value *string `mapstructure:"value" cty:"value" hcl:"value"` - Type *string `mapstructure:"type" cty:"type" hcl:"type"` -} - -// FlatMapstructure returns a new FlatArtifactParameter. -// FlatArtifactParameter is an auto-generated flat version of ArtifactParameter. -// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*ArtifactParameter) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { - return new(FlatArtifactParameter) -} - -// HCL2Spec returns the hcl spec of a ArtifactParameter. -// This spec is used by HCL to read the fields of ArtifactParameter. -// The decoded values from this spec will then be applied to a FlatArtifactParameter. -func (*FlatArtifactParameter) HCL2Spec() map[string]hcldec.Spec { - s := map[string]hcldec.Spec{ - "name": &hcldec.AttrSpec{Name: "name", Type: cty.String, Required: false}, - "value": &hcldec.AttrSpec{Name: "value", Type: cty.String, Required: false}, - "type": &hcldec.AttrSpec{Name: "type", Type: cty.String, Required: false}, - } - return s -} - -// FlatConfig is an auto-generated flat version of Config. -// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. -type FlatConfig struct { - PackerBuildName *string `mapstructure:"packer_build_name" cty:"packer_build_name" hcl:"packer_build_name"` - PackerBuilderType *string `mapstructure:"packer_builder_type" cty:"packer_builder_type" hcl:"packer_builder_type"` - PackerCoreVersion *string `mapstructure:"packer_core_version" cty:"packer_core_version" hcl:"packer_core_version"` - PackerDebug *bool `mapstructure:"packer_debug" cty:"packer_debug" hcl:"packer_debug"` - PackerForce *bool `mapstructure:"packer_force" cty:"packer_force" hcl:"packer_force"` - PackerOnError *string `mapstructure:"packer_on_error" cty:"packer_on_error" hcl:"packer_on_error"` - PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables" hcl:"packer_user_variables"` - PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables" hcl:"packer_sensitive_variables"` - CloudEnvironmentName *string `mapstructure:"cloud_environment_name" required:"false" cty:"cloud_environment_name" hcl:"cloud_environment_name"` - ClientID *string `mapstructure:"client_id" cty:"client_id" hcl:"client_id"` - ClientSecret *string `mapstructure:"client_secret" cty:"client_secret" hcl:"client_secret"` - ClientCertPath *string `mapstructure:"client_cert_path" cty:"client_cert_path" hcl:"client_cert_path"` - ClientCertExpireTimeout *string `mapstructure:"client_cert_token_timeout" required:"false" cty:"client_cert_token_timeout" hcl:"client_cert_token_timeout"` - ClientJWT *string `mapstructure:"client_jwt" cty:"client_jwt" hcl:"client_jwt"` - ObjectID *string `mapstructure:"object_id" cty:"object_id" hcl:"object_id"` - TenantID *string `mapstructure:"tenant_id" required:"false" cty:"tenant_id" hcl:"tenant_id"` - SubscriptionID *string `mapstructure:"subscription_id" cty:"subscription_id" hcl:"subscription_id"` - UseAzureCLIAuth *bool `mapstructure:"use_azure_cli_auth" required:"false" cty:"use_azure_cli_auth" hcl:"use_azure_cli_auth"` - DtlArtifacts []FlatDtlArtifact `mapstructure:"dtl_artifacts" cty:"dtl_artifacts" hcl:"dtl_artifacts"` - LabName *string `mapstructure:"lab_name" cty:"lab_name" hcl:"lab_name"` - ResourceGroupName *string `mapstructure:"lab_resource_group_name" cty:"lab_resource_group_name" hcl:"lab_resource_group_name"` - VMName *string `mapstructure:"vm_name" cty:"vm_name" hcl:"vm_name"` - PollingDurationTimeout *string `mapstructure:"polling_duration_timeout" required:"false" cty:"polling_duration_timeout" hcl:"polling_duration_timeout"` - AzureTags map[string]*string `mapstructure:"azure_tags" cty:"azure_tags" hcl:"azure_tags"` - Json map[string]interface{} `cty:"json" hcl:"json"` -} - -// FlatMapstructure returns a new FlatConfig. -// FlatConfig is an auto-generated flat version of Config. -// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { - return new(FlatConfig) -} - -// HCL2Spec returns the hcl spec of a Config. -// This spec is used by HCL to read the fields of Config. -// The decoded values from this spec will then be applied to a FlatConfig. -func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { - s := map[string]hcldec.Spec{ - "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, - "packer_builder_type": &hcldec.AttrSpec{Name: "packer_builder_type", Type: cty.String, Required: false}, - "packer_core_version": &hcldec.AttrSpec{Name: "packer_core_version", Type: cty.String, Required: false}, - "packer_debug": &hcldec.AttrSpec{Name: "packer_debug", Type: cty.Bool, Required: false}, - "packer_force": &hcldec.AttrSpec{Name: "packer_force", Type: cty.Bool, Required: false}, - "packer_on_error": &hcldec.AttrSpec{Name: "packer_on_error", Type: cty.String, Required: false}, - "packer_user_variables": &hcldec.AttrSpec{Name: "packer_user_variables", Type: cty.Map(cty.String), Required: false}, - "packer_sensitive_variables": &hcldec.AttrSpec{Name: "packer_sensitive_variables", Type: cty.List(cty.String), Required: false}, - "cloud_environment_name": &hcldec.AttrSpec{Name: "cloud_environment_name", Type: cty.String, Required: false}, - "client_id": &hcldec.AttrSpec{Name: "client_id", Type: cty.String, Required: false}, - "client_secret": &hcldec.AttrSpec{Name: "client_secret", Type: cty.String, Required: false}, - "client_cert_path": &hcldec.AttrSpec{Name: "client_cert_path", Type: cty.String, Required: false}, - "client_cert_token_timeout": &hcldec.AttrSpec{Name: "client_cert_token_timeout", Type: cty.String, Required: false}, - "client_jwt": &hcldec.AttrSpec{Name: "client_jwt", Type: cty.String, Required: false}, - "object_id": &hcldec.AttrSpec{Name: "object_id", Type: cty.String, Required: false}, - "tenant_id": &hcldec.AttrSpec{Name: "tenant_id", Type: cty.String, Required: false}, - "subscription_id": &hcldec.AttrSpec{Name: "subscription_id", Type: cty.String, Required: false}, - "use_azure_cli_auth": &hcldec.AttrSpec{Name: "use_azure_cli_auth", Type: cty.Bool, Required: false}, - "dtl_artifacts": &hcldec.BlockListSpec{TypeName: "dtl_artifacts", Nested: hcldec.ObjectSpec((*FlatDtlArtifact)(nil).HCL2Spec())}, - "lab_name": &hcldec.AttrSpec{Name: "lab_name", Type: cty.String, Required: false}, - "lab_resource_group_name": &hcldec.AttrSpec{Name: "lab_resource_group_name", Type: cty.String, Required: false}, - "vm_name": &hcldec.AttrSpec{Name: "vm_name", Type: cty.String, Required: false}, - "polling_duration_timeout": &hcldec.AttrSpec{Name: "polling_duration_timeout", Type: cty.String, Required: false}, - "azure_tags": &hcldec.AttrSpec{Name: "azure_tags", Type: cty.Map(cty.String), Required: false}, - "json": &hcldec.AttrSpec{Name: "json", Type: cty.Map(cty.String), Required: false}, - } - return s -} - -// FlatDtlArtifact is an auto-generated flat version of DtlArtifact. -// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. -type FlatDtlArtifact struct { - ArtifactName *string `mapstructure:"artifact_name" cty:"artifact_name" hcl:"artifact_name"` - ArtifactId *string `mapstructure:"artifact_id" cty:"artifact_id" hcl:"artifact_id"` - Parameters []FlatArtifactParameter `mapstructure:"parameters" cty:"parameters" hcl:"parameters"` -} - -// FlatMapstructure returns a new FlatDtlArtifact. -// FlatDtlArtifact is an auto-generated flat version of DtlArtifact. -// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*DtlArtifact) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { - return new(FlatDtlArtifact) -} - -// HCL2Spec returns the hcl spec of a DtlArtifact. -// This spec is used by HCL to read the fields of DtlArtifact. -// The decoded values from this spec will then be applied to a FlatDtlArtifact. -func (*FlatDtlArtifact) HCL2Spec() map[string]hcldec.Spec { - s := map[string]hcldec.Spec{ - "artifact_name": &hcldec.AttrSpec{Name: "artifact_name", Type: cty.String, Required: false}, - "artifact_id": &hcldec.AttrSpec{Name: "artifact_id", Type: cty.String, Required: false}, - "parameters": &hcldec.BlockListSpec{TypeName: "parameters", Nested: hcldec.ObjectSpec((*FlatArtifactParameter)(nil).HCL2Spec())}, - } - return s -} diff --git a/provisioner/azure-dtlartifact/version/version.go b/provisioner/azure-dtlartifact/version/version.go deleted file mode 100644 index 5140ec7ba..000000000 --- a/provisioner/azure-dtlartifact/version/version.go +++ /dev/null @@ -1,13 +0,0 @@ -package version - -import ( - "github.com/hashicorp/packer-plugin-sdk/version" - packerVersion "github.com/hashicorp/packer/version" -) - -var AzureDTLPluginVersion *version.PluginVersion - -func init() { - AzureDTLPluginVersion = version.InitializePluginVersion( - packerVersion.Version, packerVersion.VersionPrerelease) -} diff --git a/provisioner/salt-masterless/provisioner.go b/provisioner/salt-masterless/provisioner.go index 790f62a92..0d653fcd2 100644 --- a/provisioner/salt-masterless/provisioner.go +++ b/provisioner/salt-masterless/provisioner.go @@ -262,7 +262,7 @@ func (p *Provisioner) Provision(ctx context.Context, ui packersdk.Ui, comm packe return fmt.Errorf("Unable to create Salt state directory: %s", err) } req.Dst = path - req.Mode = getter.ModeAny + req.GetMode = getter.ModeAny if _, err := client.Get(ctx, &req); err != nil { return fmt.Errorf("Unable to download Salt formula from %s: %s", i, err) } diff --git a/website/content/docs/builders/azure/arm.mdx b/website/content/docs/builders/azure/arm.mdx deleted file mode 100644 index aabf651a4..000000000 --- a/website/content/docs/builders/azure/arm.mdx +++ /dev/null @@ -1,496 +0,0 @@ ---- -description: Packer supports building VHDs in Azure Resource manager. -page_title: Azure arm - Builders ---- - -# Azure Resource Manager Builder - -Type: `azure-arm` -Artifact BuilderId: `Azure.ResourceManagement.VMImage` - -Packer supports building Virtual Hard Disks (VHDs) and Managed Images in [Azure Resource -Manager](https://azure.microsoft.com/en-us/documentation/articles/resource-group-overview/). -Azure provides new users a [`$200` credit for the first 30 -days](https://azure.microsoft.com/en-us/free/); after which you will incur -costs for VMs built and stored using Packer. - -Azure uses a combination of OAuth and Active Directory to authorize requests to -the ARM API. Learn how to [authorize access to -ARM](/docs/builders/azure#authentication-for-azure). - -The documentation below references command output from the [Azure -CLI](https://azure.microsoft.com/en-us/documentation/articles/xplat-cli-install/). - -## Configuration Reference - -There are many configuration options available for the builder. We'll start -with authentication parameters, then go over the Azure ARM builder specific -options. In addition to the options listed here, a [communicator](/docs/templates/legacy_json_templates/communicator) can be configured for this builder. - -### Authentication options - -@include 'builder/azure/common/client/Config.mdx' - -#### Managed Identity - -If you're running Packer on an Azure VM with a [managed identity](/docs/builders/azure#azure-managed-identity) you don't need to specify any additional configuration options. As Packer will attempt to use the Managed Identity and subscription of the VM that Packer is running on. - -#### Interactive User Authentication - -To use interactive user authentication, you should specify `subscription_id` only. -Packer will use cached credentials or redirect you to a website to log in. - -#### Service Principal - -To use a [service principal](/docs/builders/azure#azure-active-directory-service-principal) -you should specify `subscription_id`, `client_id` and one of `client_secret`, -`client_cert_path` or `client_jwt`. - -- `subscription_id` (string) - Subscription under which the build will be - performed. **The service principal specified in `client_id` must have full - access to this subscription, unless build_resource_group_name option is - specified in which case it needs to have owner access to the existing - resource group specified in build_resource_group_name parameter.** - -- `client_id` (string) - The Active Directory service principal associated with - your builder. - -- `client_secret` (string) - The password or secret for your service principal. - -- `client_cert_path` (string) - The location of a PEM file containing a - certificate and private key for service principal. - -- `client_cert_token_timeout` (duration string | ex: "1h30m12s") - How long to set the expire time on the token created when using - `client_cert_path`. - -- `client_jwt` (string) - The bearer JWT assertion signed using a certificate - associated with your service principal principal. See [Azure Active - Directory docs](https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-certificate-credentials) - for more information. - -### Azure ARM builder specific options - -The Azure builder can create either a VHD or a managed image. If you are -creating a VHD, you **must** start with a VHD. Likewise, if you want to create -a managed image you **must** start with a managed image. - -### Required: - -@include 'builder/azure/arm/Config-required.mdx' - -When creating a VHD the following additional options are required: - -- `capture_container_name` (string) - Destination container name. Essentially - the "directory" where your VHD will be organized in Azure. The captured - VHD's URL will be - `https://.blob.core.windows.net/system/Microsoft.Compute/Images//.xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx.vhd`. - -- `capture_name_prefix` (string) - VHD prefix. The final artifacts will be - named `PREFIX-osDisk.UUID` and `PREFIX-vmTemplate.UUID`. - -- `resource_group_name` (string) - Resource group under which the final - artifact will be stored. - -- `storage_account` (string) - Storage account under which the final artifact - will be stored. - -When creating a managed image the following additional options are required: - -- `managed_image_name` (string) - Specify the managed image name where the - result of the Packer build will be saved. The image name must not exist - ahead of time, and will not be overwritten. If this value is set, the value - `managed_image_resource_group_name` must also be set. See - [documentation](https://docs.microsoft.com/en-us/azure/storage/storage-managed-disks-overview#images) - to learn more about managed images. - -- `managed_image_resource_group_name` (string) - Specify the managed image - resource group name where the result of the Packer build will be saved. The - resource group must already exist. If this value is set, the value - `managed_image_name` must also be set. See - [documentation](https://docs.microsoft.com/en-us/azure/storage/storage-managed-disks-overview#images) - to learn more about managed images. - -Creating a managed image using a [Shared Gallery image](https://azure.microsoft.com/en-us/blog/announcing-the-public-preview-of-shared-image-gallery/) as the source can be achieved by specifying the [shared_image_gallery](#shared_image_gallery) configuration option. - -#### Resource Group Usage - -The Azure builder can either provision resources into a new resource group that -it controls (default) or an existing one. The advantage of using a packer -defined resource group is that failed resource cleanup is easier because you -can simply remove the entire resource group, however this means that the -provided credentials must have permission to create and remove resource groups. -By using an existing resource group you can scope the provided credentials to -just this group, however failed builds are more likely to leave unused -artifacts. - -To have Packer create a resource group you **must** provide: - -- `location` (string) Azure datacenter in which your VM will build. - - CLI example `az account list-locations` - -and optionally: - -- `temp_resource_group_name` (string) name assigned to the temporary resource - group created during the build. If this value is not set, a random value - will be assigned. This resource group is deleted at the end of the build. - -To use an existing resource group you **must** provide: - -- `build_resource_group_name` (string) - Specify an existing resource group - to run the build in. - -Providing `temp_resource_group_name` or `location` in combination with -`build_resource_group_name` is not allowed. - -### Optional: - -@include 'builder/azure/arm/Config-not-required.mdx' - -@include 'builder/azure/common/client/Config-not-required.mdx' - -### Communicator Config - -In addition to the builder options, a communicator may also be defined: - -@include 'packer-plugin-sdk/communicator/Config-not-required.mdx' - -@include 'packer-plugin-sdk/communicator/SSH-not-required.mdx' - -@include 'packer-plugin-sdk/communicator/SSH-Private-Key-File-not-required.mdx' - -## Basic Example - -Here is a basic example for Azure. - - - - -```hcl -source "azure-arm" "basic-example" { - client_id = "fe354398-d7sf-4dc9-87fd-c432cd8a7e09" - client_secret = "keepitsecret&#*$" - resource_group_name = "packerdemo" - storage_account = "virtualmachines" - subscription_id = "44cae533-4247-4093-42cf-897ded6e7823" - tenant_id = "de39842a-caba-497e-a798-7896aea43218" - - capture_container_name = "images" - capture_name_prefix = "packer" - - os_type = "Linux" - image_publisher = "Canonical" - image_offer = "UbuntuServer" - image_sku = "14.04.4-LTS" - - azure_tags = { - dept = "engineering" - } - - location = "West US" - vm_size = "Standard_A2" -} - -build { - sources = ["sources.azure-arm.basic-example"] -} -``` - - - - -```json -{ - "type": "azure-arm", - - "client_id": "fe354398-d7sf-4dc9-87fd-c432cd8a7e09", - "client_secret": "keepitsecret&#*$", - "resource_group_name": "packerdemo", - "storage_account": "virtualmachines", - "subscription_id": "44cae533-4247-4093-42cf-897ded6e7823", - "tenant_id": "de39842a-caba-497e-a798-7896aea43218", - - "capture_container_name": "images", - "capture_name_prefix": "packer", - - "os_type": "Linux", - "image_publisher": "Canonical", - "image_offer": "UbuntuServer", - "image_sku": "14.04.4-LTS", - - "azure_tags": { - "dept": "engineering" - }, - - "location": "West US", - "vm_size": "Standard_A2" -} -``` - - - - -## Deprovision - -Azure VMs should be deprovisioned at the end of every build. For Windows this -means executing sysprep, and for Linux this means executing the waagent -deprovision process. - -Please refer to the Azure -[examples](https://github.com/hashicorp/packer/tree/master/builder/azure/examples) for -complete examples showing the deprovision process. - -### Windows - -The following provisioner snippet shows how to sysprep a Windows VM. -Deprovision should be the last operation executed by a build. The code below -will wait for sysprep to write the image status in the registry and will exit -after that. The possible states, in case you want to wait for another state, -[are documented -here](https://technet.microsoft.com/en-us/library/hh824815.aspx) - - - - -```json -{ - "provisioners": [ - { - "type": "powershell", - "inline": [ - " # NOTE: the following *3* lines are only needed if the you have installed the Guest Agent.", - " while ((Get-Service RdAgent).Status -ne 'Running') { Start-Sleep -s 5 }", - " while ((Get-Service WindowsAzureTelemetryService).Status -ne 'Running') { Start-Sleep -s 5 }", - " while ((Get-Service WindowsAzureGuestAgent).Status -ne 'Running') { Start-Sleep -s 5 }", - - "& $env:SystemRoot\\System32\\Sysprep\\Sysprep.exe /oobe /generalize /quiet /quit /mode:vm", - "while($true) { $imageState = Get-ItemProperty HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup\\State | Select ImageState; if($imageState.ImageState -ne 'IMAGE_STATE_GENERALIZE_RESEAL_TO_OOBE') { Write-Output $imageState.ImageState; Start-Sleep -s 10 } else { break } }" - ] - } - ] -} -``` - - - - -```hcl -provisioner "powershell" { - inline = [ - " # NOTE: the following *3* lines are only needed if the you have installed the Guest Agent.", - " while ((Get-Service RdAgent).Status -ne 'Running') { Start-Sleep -s 5 }", - " while ((Get-Service WindowsAzureTelemetryService).Status -ne 'Running') { Start-Sleep -s 5 }", - " while ((Get-Service WindowsAzureGuestAgent).Status -ne 'Running') { Start-Sleep -s 5 }", - - "& $env:SystemRoot\\System32\\Sysprep\\Sysprep.exe /oobe /generalize /quiet /quit /mode:vm", - "while($true) { $imageState = Get-ItemProperty HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup\\State | Select ImageState; if($imageState.ImageState -ne 'IMAGE_STATE_GENERALIZE_RESEAL_TO_OOBE') { Write-Output $imageState.ImageState; Start-Sleep -s 10 } else { break } }" - ] -} -``` - - - - -The Windows Guest Agent participates in the Sysprep process. The agent must be -fully installed before the VM can be sysprep'ed. To ensure this is true all -agent services must be running before executing sysprep.exe. The above JSON -snippet shows one way to do this in the PowerShell provisioner. This snippet is -**only** required if the VM is configured to install the agent, which is the -default. To learn more about disabling the Windows Guest Agent please see -[Install the VM -Agent](https://docs.microsoft.com/en-us/azure/virtual-machines/extensions/agent-windows#install-the-vm-agent). - -Please note that sysprep can get stuck in infinite loops if it is not configured -correctly -- for example, if it is waiting for a reboot that you never perform. - -### Linux - -The following provisioner snippet shows how to deprovision a Linux VM. -Deprovision should be the last operation executed by a build. - - - - -```json -{ - "provisioners": [ - { - "execute_command": "chmod +x {{ .Path }}; {{ .Vars }} sudo -E sh '{{ .Path }}'", - "inline": [ - "/usr/sbin/waagent -force -deprovision+user && export HISTSIZE=0 && sync" - ], - "inline_shebang": "/bin/sh -x", - "type": "shell" - } - ] -} -``` - - - - -```hcl -provisioner "shell" { - execute_command = "chmod +x {{ .Path }}; {{ .Vars }} sudo -E sh '{{ .Path }}'" - inline = [ - "/usr/sbin/waagent -force -deprovision+user && export HISTSIZE=0 && sync" - ] - inline_shebang = "/bin/sh -x" -} -``` - - - - -To learn more about the Linux deprovision process please see WALinuxAgent's -[README](https://github.com/Azure/WALinuxAgent/blob/master/README.md). - -#### skip_clean - -Customers have reported issues with the deprovision process where the builder -hangs. The error message is similar to the following. - - Build 'azure-arm' errored: Retryable error: Error removing temporary script at /tmp/script_9899.sh: ssh: handshake failed: EOF - -One solution is to set skip_clean to true in the provisioner. This prevents -Packer from cleaning up any helper scripts uploaded to the VM during the build. - -## Defaults - -The Azure builder attempts to pick default values that provide for a just works -experience. These values can be changed by the user to more suitable values. - -- The default user name is Packer not root as in other builders. Most distros - on Azure do not allow root to SSH to a VM hence the need for a non-root - default user. Set the ssh_username option to override the default value. -- The default VM size is Standard_A1. Set the vm_size option to override - the default value. -- The default image version is latest. Set the image_version option to - override the default value. -- By default a temporary resource group will be created and destroyed as part - of the build. If you do not have permissions to do so, use - `build_resource_group_name` to specify an existing resource group to run - the build in. - -## Implementation - -~> **Warning!** This is an advanced topic. You do not need to understand -the implementation to use the Azure builder. - -The Azure builder uses ARM -[templates](https://azure.microsoft.com/en-us/documentation/articles/resource-group-authoring-templates/) -to deploy resources. ARM templates allow you to express the what without having -to express the how. - -The Azure builder works under the assumption that it creates everything it -needs to execute a build. When the build has completed it simply deletes the -resource group to cleanup any runtime resources. Resource groups are named -using the form `packer-Resource-Group-`. The value `` is a -random value that is generated at every invocation of packer. The `` -value is re-used as much as possible when naming resources, so users can better -identify and group these transient resources when seen in their subscription. - -> The VHD is created on a user specified storage account, not a random one -> created at runtime. When a virtual machine is captured the resulting VHD is -> stored on the same storage account as the source VHD. The VHD created by -> Packer must persist after a build is complete, which is why the storage -> account is set by the user. - -The basic steps for a build are: - -1. Create a resource group. -2. Validate and deploy a VM template. -3. Execute provision - defined by the user; typically shell commands. -4. Power off and capture the VM. -5. Delete the resource group. -6. Delete the temporary VM's OS disk. - -The templates used for a build are currently fixed in the code. There is a -template for Linux, Windows, and KeyVault. The templates are themselves -templated with place holders for names, passwords, SSH keys, certificates, etc. - -### What's Randomized? - -The Azure builder creates the following random values at runtime. - -- Administrator Password: a random 32-character value using the _password - alphabet_. -- Certificate: a 2,048-bit certificate used to secure WinRM communication. - The certificate is valid for 24-hours, which starts roughly at invocation - time. -- Certificate Password: a random 32-character value using the _password - alphabet_ used to protect the private key of the certificate. -- Compute Name: a random 15-character name prefixed with pkrvm; the name of - the VM. -- Deployment Name: a random 15-character name prefixed with pkfdp; the name - of the deployment. -- KeyVault Name: a random 15-character name prefixed with pkrkv. -- NIC Name: a random 15-character name prefixed with pkrni. -- Public IP Name: a random 15-character name prefixed with pkrip. -- OS Disk Name: a random 15-character name prefixed with pkros. -- Data Disk Name: a random 15-character name prefixed with pkrdd. -- Resource Group Name: a random 33-character name prefixed with - packer-Resource-Group-. -- Subnet Name: a random 15-character name prefixed with pkrsn. -- SSH Key Pair: a 2,048-bit asymmetric key pair; can be overridden by the - user. -- Virtual Network Name: a random 15-character name prefixed with pkrvn. - -The default alphabet used for random values is -**0123456789bcdfghjklmnpqrstvwxyz**. The alphabet was reduced (no vowels) to -prevent running afoul of Azure decency controls. - -The password alphabet used for random values is -**0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ**. - -### Deprecation Warning - -You may see a scary-looking deprecation warning when you run the Azure builder: - -``` -==> azure-arm: Warning: You are using Azure Packer Builder to create VHDs which -is being deprecated, consider using Managed Images. Learn more -http://aka.ms/packermanagedimage -``` - -Don't panic. Your build won't stop working next week. - -Long-term, Azure wants everyone to move to using managed images and managed -disks because they hide the complexity with respect to storage account -performance. Managed disks can be exported to a VHD. If this is deprecated, -it will be done in a transparent process by the Microsoft team who help -maintain Packer. - -In the future, Packer may remove VHD support but add a post-processor that can -automate the export to a storage account. - -### Windows - -The Windows implementation is very similar to the Linux build, with the -exception that it deploys a template to configure KeyVault. Packer communicates -with a Windows VM using the WinRM protocol. Windows VMs on Azure default to -using both password and certificate based authentication for WinRM. The -password is easily set via the VM ARM template, but the certificate requires an -intermediary. The intermediary for Azure is KeyVault. The certificate is -uploaded to a new KeyVault provisioned in the same resource group as the VM. -When the Windows VM is deployed, it links to the certificate in KeyVault, and -Azure will ensure the certificate is injected as part of deployment. - -The basic steps for a Windows build are: - -1. Create a resource group. -2. Validate and deploy a KeyVault template. -3. Validate and deploy a VM template. -4. Execute provision - defined by the user; typically shell commands. -5. Power off and capture the VM. -6. Delete the resource group. -7. Delete the temporary VM's OS disk. - -A Windows build requires two templates and two deployments. Unfortunately, the -KeyVault and VM cannot be deployed at the same time hence the need for two -templates and deployments. The time required to deploy a KeyVault template is -minimal, so overall impact is small. - -See the -[examples/azure](https://github.com/hashicorp/packer/tree/master/builder/azure/examples) -folder in the Packer project for more examples. diff --git a/website/content/docs/builders/azure/chroot.mdx b/website/content/docs/builders/azure/chroot.mdx deleted file mode 100644 index bd5caca17..000000000 --- a/website/content/docs/builders/azure/chroot.mdx +++ /dev/null @@ -1,295 +0,0 @@ ---- -description: > - The azure-chroot Packer builder is able to create Azure Managed Images - leveraging - - a VM in Azure. -page_title: Azure chroot - Builders ---- - -# Azure Builder (chroot) - -Type: `azure-chroot` -Artifact BuilderId: `azure.chroot` - -The `azure-chroot` builder is able to build Azure managed disk (MD) images. For -more information on managed disks, see [Azure Managed Disks Overview](https://docs.microsoft.com/en-us/azure/virtual-machines/windows/managed-disks-overview). - -The difference between this builder and the `azure-arm` builder is that this -builder is able to build a managed disk image without launching a new Azure VM -for every build, but instead use an already-running Azure VM. This can -dramatically speed up image builds. It also allows for more deterministic image -content and enables some capabilities that are not possible with the -`azure-arm` builder. - -> **This is an advanced builder** If you're just getting started with Packer, -> it is recommend to start with the [azure-arm builder](/docs/builders/azure-arm), -> which is much easier to use. - -## How Does it Work? - -This builder works by creating a new MD from either an existing source or from -scratch and attaching it to the (already existing) Azure VM where Packer is -running. Once attached, a [chroot](https://en.wikipedia.org/wiki/Chroot) is set -up and made available to the [provisioners](/docs/provisioners). -After provisioning, the MD is detached, snapshotted and a MD image is created. - -Using this process, minutes can be shaved off the image creation process -because Packer does not need to launch a VM instance. - -There are some restrictions however: - -- The host system must be a similar system (generally the same OS version, - kernel versions, etc.) as the image being built. -- If the source is a managed disk, it must be made available in the same - region as the host system. -- The host system SKU has to allow for all of the specified disks to be - attached. - -## Configuration Reference - -There are many configuration options available for the builder. We'll start -with authentication parameters, then go over the Azure chroot builder specific -options. - -### Authentication options - -None of the authentication options are required, but depending on which -ones are specified a different authentication method may be used. See the -[shared Azure builders documentation](/docs/builders/azure) for more -information. - -@include 'builder/azure/common/client/Config-not-required.mdx' - -### Azure chroot builder specific options - -#### Required: - -@include 'builder/azure/chroot/Config-required.mdx' - -#### Optional: - -@include 'builder/azure/chroot/Config-not-required.mdx' - -#### Output options: - -At least one of these options needs to be specified: - -- `image_resource_id` (string) - The managed image to create using this build. - -- `shared_image_destination` (object) - The shared image to create using this build. - -Where `shared_image_destination` is an object with the following properties: - -@include 'builder/azure/chroot/SharedImageGalleryDestination-required.mdx' - -@include 'builder/azure/chroot/SharedImageGalleryDestination-not-required.mdx' - -And `target_regions` is an array of objects with the following properties: - -@include 'builder/azure/chroot/TargetRegion-required.mdx' - -@include 'builder/azure/chroot/TargetRegion-not-required.mdx' - -## Chroot Mounts - -The `chroot_mounts` configuration can be used to mount specific devices within -the chroot. By default, the following additional mounts are added into the -chroot by Packer: - -- `/proc` (proc) -- `/sys` (sysfs) -- `/dev` (bind to real `/dev`) -- `/dev/pts` (devpts) -- `/proc/sys/fs/binfmt_misc` (binfmt_misc) - -These default mounts are usually good enough for anyone and are sane defaults. -However, if you want to change or add the mount points, you may using the -`chroot_mounts` configuration. Here is an example configuration which only -mounts `/prod` and `/dev`: - -```json -{ - "chroot_mounts": [ - ["proc", "proc", "/proc"], - ["bind", "/dev", "/dev"] - ] -} -``` - -`chroot_mounts` is a list of a 3-tuples of strings. The three components of the -3-tuple, in order, are: - -- The filesystem type. If this is "bind", then Packer will properly bind the - filesystem to another mount point. - -- The source device. - -- The mount directory. - -## Additional template function - -Because this builder runs on an Azure VM, there is an additional template function -available called `vm`, which returns the following VM metadata: - -- name -- subscription_id -- resource_group -- location -- resource_id - -This function can be used in the configuration templates, for example, use - -```text -"{{ vm `subscription_id` }}" -``` - -to fill in the subscription ID of the VM in any of the configuration options. - -## Examples - -Here are some examples using this builder. -This builder requires privileged actions, such as mounting disks, running -`chroot` and other admin commands. Usually it needs to be run with root -permissions, for example: - -```shell-session -$ sudo -E packer build example.pkr.json -``` - -### Using a VM with a Managed Identity - -On a VM with a system-assigned managed identity that has the contributor role -on its own resource group, the following config can be used to create an -updated Debian image: - - - - - -```hcl -source "azure-chroot" "example" { - image_resource_id = "/subscriptions/{{vm `subscription_id`}}/resourceGroups/{{vm `resource_group`}}/providers/Microsoft.Compute/images/MyDebianOSImage-{{timestamp}}" - source = "credativ:Debian:9:latest" -} - -build { - sources = ["source.azure-chroot.example"] - - provisioner "shell" { - inline = ["apt-get update", "apt-get upgrade -y"] - inline_shebang = "/bin/sh -x" - } -} -``` - - - - - -```json -{ - "builders": [ - { - "type": "azure-chroot", - - "image_resource_id": "/subscriptions/{{vm `subscription_id`}}/resourceGroups/{{vm `resource_group`}}/providers/Microsoft.Compute/images/MyDebianOSImage-{{timestamp}}", - "source": "credativ:Debian:9:latest" - } - ], - "provisioners": [ - { - "inline": ["apt-get update", "apt-get upgrade -y"], - "inline_shebang": "/bin/sh -x", - "type": "shell" - } - ] -} -``` - - - - -### Using a Service Principal - -Here is an example that creates a Debian image with updated packages. Specify -all environment variables (`ARM_CLIENT_ID`, `ARM_CLIENT_SECRET`, -`ARM_SUBSCRIPTION_ID`) to use a service principal. -The identity you choose should have permission to create disks and images and also -to update your VM. -Set the `ARM_IMAGE_RESOURCEGROUP_ID` variable to an existing resource group in the -subscription where the resulting image will be created. - - - - -```hcl -variable "client_id" { - type = string -} -variable "client_secret" { - type = string -} -variable "subscription_id" { - type = string -} -variable "resource_group" { - type = string -} - -source "azure-chroot" "basic-example" { - client_id = var.client_id - client_secret = var.client_secret - subscription_id = var.subscription_id - - image_resource_id = "/subscriptions/${var.subscription_id}/resourceGroups/${var.resource_group}/providers/Microsoft.Compute/images/MyDebianOSImage-{{timestamp}}" - - source = "credativ:Debian:9:latest" -} - -build { - sources = ["sources.azure-chroot.basic-example"] - - provisioner "shell" { - inline = ["apt-get update", "apt-get upgrade -y"] - inline_shebang = "/bin/sh -x" - } -} -``` - - - - -```json -{ - "variables": { - "client_id": "{{env `ARM_CLIENT_ID`}}", - "client_secret": "{{env `ARM_CLIENT_SECRET`}}", - "subscription_id": "{{env `ARM_SUBSCRIPTION_ID`}}", - "resource_group": "{{env `ARM_IMAGE_RESOURCEGROUP_ID`}}" - }, - "builders": [ - { - "type": "azure-chroot", - - "client_id": "{{user `client_id`}}", - "client_secret": "{{user `client_secret`}}", - "subscription_id": "{{user `subscription_id`}}", - - "image_resource_id": "/subscriptions/{{user `subscription_id`}}/resourceGroups/{{user `resource_group`}}/providers/Microsoft.Compute/images/MyDebianOSImage-{{timestamp}}", - - "source": "credativ:Debian:9:latest" - } - ], - "provisioners": [ - { - "inline": ["apt-get update", "apt-get upgrade -y"], - "inline_shebang": "/bin/sh -x", - "type": "shell" - } - ] -} -``` - - - diff --git a/website/content/docs/builders/azure/index.mdx b/website/content/docs/builders/azure/index.mdx deleted file mode 100644 index 4999c0112..000000000 --- a/website/content/docs/builders/azure/index.mdx +++ /dev/null @@ -1,117 +0,0 @@ ---- -description: > - Packer is able to create Azure VM images. To achieve this, Packer comes with - - multiple builders depending on the strategy you want to use to build the - images. -page_title: Azure images - Builders ---- - -# Azure Virtual Machine Image Builders - -Packer can create Azure virtual machine images through variety of ways -depending on the strategy that you want to use for building the images. -Packer supports the following builders for Azure images at the moment: - -- [azure-arm](/docs/builders/azure-arm) - Uses Azure Resource - Manager (ARM) to launch a virtual machine (VM) from which a new image is - captured after provisioning. If in doubt, use this builder; it is the - easiest builder to get started with. - -- [azure-chroot](/docs/builders/azure-chroot) - Uses ARM to create - a managed disk that is attached to an existing Azure VM that Packer is - running on. Provisioning leverages [Chroot](https://en.wikipedia.org/wiki/Chroot) - environment. After provisioning, the disk is detached an image is created - from this disk. This is an **advanced builder and should not be used by - newcomers**. However, it is also the fastest way to build a VM image in - Azure. - --> **Don't know which builder to use?** If in doubt, use the [azure-arm -builder](/docs/builders/azure-arm). It is much easier to use. - -# Authentication for Azure - -The Packer Azure builders provide a couple of ways to authenticate to Azure. The -following methods are available and are explained below: - -- Azure Active Directory interactive login. Interactive login is available - for the Public and US Gov clouds only. -- Azure Managed Identity -- Azure Active Directory Service Principal -- Azure CLI - --> **Don't know which authentication method to use?** Go with interactive -login to try out the builders. If you need Packer to run automatically, -switch to using a Service Principal or Managed Identity. - -No matter which method you choose, the identity you use will need the -appropriate permissions on Azure resources for Packer to operate. The minimal -set of permissions is highly dependent on the builder and its configuration. -An easy way to get started is to assign the identity the `Contributor` role at -the subscription level. - -## Azure Active Directory interactive login - -If your organization allows it, you can use a command line interactive login -method based on oAuth 'device code flow'. Packer will select this method when -you only specify a `subscription_id` in your builder configuration. When you -run Packer, it will ask you to visit a web site and input a code. This web site -will then authenticate you, satisfying any two-factor authentication policies -that your organization might have. The tokens are cached under the `.azure/packer` -directory in your home directory and will be reused if they are still valid -on subsequent runs. - -Please note that the interactive login is only available on the Azure public -cloud, not on sovereign/government clouds. - -## Azure Managed Identity - -Azure provides the option to assign an identity to a virtual machine ([Azure -documentation](https://docs.microsoft.com/en-us/azure/active-directory/managed-identities-azure-resources/qs-configure-portal-windows-vm)). Packer can -use a system assigned identity for a VM where Packer is running to orchestrate -Azure API's. This is the default behavior and requires no configuration -properties to be set. It does, however, require that you run Packer on an -Azure VM. - -To enable this method, [let Azure assign a system-assigned identity to your VM](https://docs.microsoft.com/en-us/azure/active-directory/managed-identities-azure-resources/qs-configure-portal-windows-vm). -Then, [grant your VM access to the appropriate resources](https://docs.microsoft.com/en-us/azure/active-directory/managed-identities-azure-resources/howto-assign-access-portal). -To get started, try assigning the `Contributor` role at the subscription level to -your VM. Then, when you discover your exact scenario, scope the permissions -appropriately or isolate Packer builds in a separate subscription. - -## Azure Active Directory Service Principal - -Azure Active Directory models service accounts as 'Service Principal' (SP) -objects. An SP represents an application accessing your Azure resources. It -is identified by a client ID (aka application ID) and can use a password or a -certificate to authenticate. To use a Service Principal, specify the -`subscription_id` and `client_id`, as well as either `client_secret`, -`client_cert_path` or `client_jwt`. Each of these last three represent a different -way to authenticate the SP to AAD: - -- `client_secret` - allows the user to provide a password/secret registered - for the AAD SP. -- `client_cert_path` - allows usage of a certificate to be used to - authenticate as the specified AAD SP. -- `client_cert_token_timeout` - How long to set the expire time on the token created when using - `client_cert_path`. -- `client_jwt` - For advanced scenario's where the used cannot provide Packer - the full certificate, they can provide a JWT bearer token for client auth - (RFC 7523, Sec. 2.2). These bearer tokens are created and signed using a - certificate registered in AAD and have a user-chosen expiry time, limiting - the validity of the token. This is also the underlying mechanism used to - authenticate when using `client_cert_path`. - -To create a service principal, you can follow [the Azure documentation on this -subject](https://docs.microsoft.com/en-us/cli/azure/create-an-azure-service-principal-azure-cli?view=azure-cli-latest). - -## Azure CLI - -This method will skip all other options provided and only use the credentials that the az cli is authenticated with. -Works with both normal user (`az login`) as well as service principal (`az login --service-principal --username APP_ID --password PASSWORD --tenant TENANT_ID`). - -To enable az cli authentication, use the following: - -- `"use_azure_cli_auth": true` - -This mode will use the `tenant_id` and `subscription_id` from the current active az session which can be found by running: `az account show` diff --git a/website/content/partials/builder/azure/arm/Config-not-required.mdx b/website/content/partials/builder/azure/arm/Config-not-required.mdx deleted file mode 100644 index 7ccb714c5..000000000 --- a/website/content/partials/builder/azure/arm/Config-not-required.mdx +++ /dev/null @@ -1,309 +0,0 @@ - - -- `user_assigned_managed_identities` ([]string) - A list of one or more fully-qualified resource IDs of user assigned - managed identities to be configured on the VM. - See [documentation](https://docs.microsoft.com/en-us/azure/active-directory/managed-identities-azure-resources/how-to-use-vm-token) - for how to acquire tokens within the VM. - To assign a user assigned managed identity to a VM, the provided account or service principal must have [Managed Identity Operator](https://docs.microsoft.com/en-us/azure/role-based-access-control/built-in-roles#managed-identity-operator) - and [Virtual Machine Contributor](https://docs.microsoft.com/en-us/azure/role-based-access-control/built-in-roles#virtual-machine-contributor) role assignments. - -- `capture_name_prefix` (string) - VHD prefix. - -- `capture_container_name` (string) - Destination container name. - -- `shared_image_gallery` (SharedImageGallery) - Use a [Shared Gallery - image](https://azure.microsoft.com/en-us/blog/announcing-the-public-preview-of-shared-image-gallery/) - as the source for this build. *VHD targets are incompatible with this - build type* - the target must be a *Managed Image*. When using shared_image_gallery as a source, image_publisher, - image_offer, image_sku, image_version, and custom_managed_image_name should not be set. - - In JSON - ```json - "shared_image_gallery": { - "subscription": "00000000-0000-0000-0000-00000000000", - "resource_group": "ResourceGroup", - "gallery_name": "GalleryName", - "image_name": "ImageName", - "image_version": "1.0.0" - } - "managed_image_name": "TargetImageName", - "managed_image_resource_group_name": "TargetResourceGroup" - ``` - In HCL2 - ```hcl - shared_image_gallery { - subscription = "00000000-0000-0000-0000-00000000000" - resource_group = "ResourceGroup" - gallery_name = "GalleryName" - image_name = "ImageName" - image_version = "1.0.0" - } - managed_image_name = "TargetImageName" - managed_image_resource_group_name = "TargetResourceGroup" - ``` - -- `shared_image_gallery_destination` (SharedImageGalleryDestination) - The name of the Shared Image Gallery under which the managed image will be published as Shared Gallery Image version. - - Following is an example. - - In JSON - ```json - "shared_image_gallery_destination": { - "subscription": "00000000-0000-0000-0000-00000000000", - "resource_group": "ResourceGroup", - "gallery_name": "GalleryName", - "image_name": "ImageName", - "image_version": "1.0.0", - "replication_regions": ["regionA", "regionB", "regionC"], - "storage_account_type": "Standard_LRS" - } - "managed_image_name": "TargetImageName", - "managed_image_resource_group_name": "TargetResourceGroup" - ``` - In HCL2 - ```hcl - shared_image_gallery_destination { - subscription = "00000000-0000-0000-0000-00000000000" - resource_group = "ResourceGroup" - gallery_name = "GalleryName" - image_name = "ImageName" - image_version = "1.0.0" - replication_regions = ["regionA", "regionB", "regionC"] - storage_account_type = "Standard_LRS" - } - managed_image_name = "TargetImageName" - managed_image_resource_group_name = "TargetResourceGroup" - ``` - -- `shared_image_gallery_timeout` (duration string | ex: "1h5m2s") - How long to wait for an image to be published to the shared image - gallery before timing out. If your Packer build is failing on the - Publishing to Shared Image Gallery step with the error `Original Error: - context deadline exceeded`, but the image is present when you check your - Azure dashboard, then you probably need to increase this timeout from - its default of "60m" (valid time units include `s` for seconds, `m` for - minutes, and `h` for hours.) - -- `shared_gallery_image_version_end_of_life_date` (string) - The end of life date (2006-01-02T15:04:05.99Z) of the gallery Image Version. This property - can be used for decommissioning purposes. - -- `shared_image_gallery_replica_count` (int32) - The number of replicas of the Image Version to be created per region. This - property would take effect for a region when regionalReplicaCount is not specified. - Replica count must be between 1 and 10. - -- `shared_gallery_image_version_exclude_from_latest` (bool) - If set to true, Virtual Machines deployed from the latest version of the - Image Definition won't use this Image Version. - -- `image_version` (string) - Specify a specific version of an OS to boot from. - Defaults to `latest`. There may be a difference in versions available - across regions due to image synchronization latency. To ensure a consistent - version across regions set this value to one that is available in all - regions where you are deploying. - - CLI example - `az vm image list --location westus --publisher Canonical --offer UbuntuServer --sku 16.04.0-LTS --all` - -- `location` (string) - Azure datacenter in which your VM will build. - -- `vm_size` (string) - Size of the VM used for building. This can be changed when you deploy a - VM from your VHD. See - [pricing](https://azure.microsoft.com/en-us/pricing/details/virtual-machines/) - information. Defaults to `Standard_A1`. - - CLI example `az vm list-sizes --location westus` - -- `managed_image_resource_group_name` (string) - Specify the managed image resource group name where the result of the - Packer build will be saved. The resource group must already exist. If - this value is set, the value managed_image_name must also be set. See - documentation to learn more about managed images. - -- `managed_image_name` (string) - Specify the managed image name where the result of the Packer build will - be saved. The image name must not exist ahead of time, and will not be - overwritten. If this value is set, the value - managed_image_resource_group_name must also be set. See documentation to - learn more about managed images. - -- `managed_image_storage_account_type` (string) - Specify the storage account - type for a managed image. Valid values are Standard_LRS and Premium_LRS. - The default is Standard_LRS. - -- `managed_image_os_disk_snapshot_name` (string) - If - managed_image_os_disk_snapshot_name is set, a snapshot of the OS disk - is created with the same name as this value before the VM is captured. - -- `managed_image_data_disk_snapshot_prefix` (string) - If - managed_image_data_disk_snapshot_prefix is set, snapshot of the data - disk(s) is created with the same prefix as this value before the VM is - captured. - -- `keep_os_disk` (bool) - If - keep_os_disk is set, the OS disk is not deleted. - The default is false. - -- `managed_image_zone_resilient` (bool) - Store the image in zone-resilient storage. You need to create it in a - region that supports [availability - zones](https://docs.microsoft.com/en-us/azure/availability-zones/az-overview). - -- `azure_tags` (map[string]string) - Name/value pair tags to apply to every resource deployed i.e. Resource - Group, VM, NIC, VNET, Public IP, KeyVault, etc. The user can define up - to 15 tags. Tag names cannot exceed 512 characters, and tag values - cannot exceed 256 characters. - -- `azure_tag` ([]{name string, value string}) - Same as [`azure_tags`](#azure_tags) but defined as a singular repeatable block - containing a `name` and a `value` field. In HCL2 mode the - [`dynamic_block`](/docs/templates/hcl_templates/expressions#dynamic-blocks) - will allow you to create those programatically. - -- `resource_group_name` (string) - Resource group under which the final artifact will be stored. - -- `storage_account` (string) - Storage account under which the final artifact will be stored. - -- `temp_compute_name` (string) - temporary name assigned to the VM. If this - value is not set, a random value will be assigned. Knowing the resource - group and VM name allows one to execute commands to update the VM during a - Packer build, e.g. attach a resource disk to the VM. - -- `temp_nic_name` (string) - temporary name assigned to the Nic. If this - value is not set, a random value will be assigned. Being able to assign a custom - nicname could ease deployment if naming conventions are used. - -- `temp_resource_group_name` (string) - name assigned to the temporary resource group created during the build. - If this value is not set, a random value will be assigned. This resource - group is deleted at the end of the build. - -- `build_resource_group_name` (string) - Specify an existing resource group to run the build in. - -- `build_key_vault_name` (string) - Specify an existing key vault to use for uploading certificates to the - instance to connect. - -- `build_key_vault_sku` (string) - Specify the KeyVault SKU to create during the build. Valid values are - standard or premium. The default value is standard. - -- `private_virtual_network_with_public_ip` (bool) - This value allows you to - set a virtual_network_name and obtain a public IP. If this value is not - set and virtual_network_name is defined Packer is only allowed to be - executed from a host on the same subnet / virtual network. - -- `virtual_network_name` (string) - Use a pre-existing virtual network for the - VM. This option enables private communication with the VM, no public IP - address is used or provisioned (unless you set - private_virtual_network_with_public_ip). - -- `virtual_network_subnet_name` (string) - If virtual_network_name is set, - this value may also be set. If virtual_network_name is set, and this - value is not set the builder attempts to determine the subnet to use with - the virtual network. If the subnet cannot be found, or it cannot be - disambiguated, this value should be set. - -- `virtual_network_resource_group_name` (string) - If virtual_network_name is - set, this value may also be set. If virtual_network_name is set, and - this value is not set the builder attempts to determine the resource group - containing the virtual network. If the resource group cannot be found, or - it cannot be disambiguated, this value should be set. - -- `custom_data_file` (string) - Specify a file containing custom data to inject into the cloud-init - process. The contents of the file are read and injected into the ARM - template. The custom data will be passed to cloud-init for processing at - the time of provisioning. See - [documentation](http://cloudinit.readthedocs.io/en/latest/topics/examples.html) - to learn more about custom data, and how it can be used to influence the - provisioning process. - -- `plan_info` (PlanInformation) - Used for creating images from Marketplace images. Please refer to - [Deploy an image with Marketplace - terms](https://aka.ms/azuremarketplaceapideployment) for more details. - Not all Marketplace images support programmatic deployment, and support - is controlled by the image publisher. - - An example plan\_info object is defined below. - - ```json - { - "plan_info": { - "plan_name": "rabbitmq", - "plan_product": "rabbitmq", - "plan_publisher": "bitnami" - } - } - ``` - - `plan_name` (string) - The plan name, required. `plan_product` (string) - - The plan product, required. `plan_publisher` (string) - The plan publisher, - required. `plan_promotion_code` (string) - Some images accept a promotion - code, optional. - - Images created from the Marketplace with `plan_info` **must** specify - `plan_info` whenever the image is deployed. The builder automatically adds - tags to the image to ensure this information is not lost. The following - tags are added. - - ```text - 1. PlanName - 2. PlanProduct - 3. PlanPublisher - 4. PlanPromotionCode - ``` - -- `polling_duration_timeout` (duration string | ex: "1h5m2s") - The default PollingDuration for azure is 15mins, this property will override - that value. See [Azure DefaultPollingDuration](https://godoc.org/github.com/Azure/go-autorest/autorest#pkg-constants) - If your Packer build is failing on the - ARM deployment step with the error `Original Error: - context deadline exceeded`, then you probably need to increase this timeout from - its default of "15m" (valid time units include `s` for seconds, `m` for - minutes, and `h` for hours.) - -- `os_type` (string) - If either Linux or Windows is specified Packer will - automatically configure authentication credentials for the provisioned - machine. For Linux this configures an SSH authorized key. For Windows - this configures a WinRM certificate. - -- `temp_os_disk_name` (string) - temporary name assigned to the OSDisk. If this - value is not set, a random value will be assigned. Being able to assign a custom - osDiskName could ease deployment if naming conventions are used. - -- `os_disk_size_gb` (int32) - Specify the size of the OS disk in GB - (gigabytes). Values of zero or less than zero are ignored. - -- `disk_additional_size` ([]int32) - The size(s) of any additional hard disks for the VM in gigabytes. If - this is not specified then the VM will only contain an OS disk. The - number of additional disks and maximum size of a disk depends on the - configuration of your VM. See - [Windows](https://docs.microsoft.com/en-us/azure/virtual-machines/windows/about-disks-and-vhds) - or - [Linux](https://docs.microsoft.com/en-us/azure/virtual-machines/linux/about-disks-and-vhds) - for more information. - - For VHD builds the final artifacts will be named - `PREFIX-dataDisk-.UUID.vhd` and stored in the specified capture - container along side the OS disk. The additional disks are included in - the deployment template `PREFIX-vmTemplate.UUID`. - - For Managed build the final artifacts are included in the managed image. - The additional disk will have the same storage account type as the OS - disk, as specified with the `managed_image_storage_account_type` - setting. - -- `disk_caching_type` (string) - Specify the disk caching type. Valid values - are None, ReadOnly, and ReadWrite. The default value is ReadWrite. - -- `allowed_inbound_ip_addresses` ([]string) - Specify the list of IP addresses and CIDR blocks that should be - allowed access to the VM. If provided, an Azure Network Security - Group will be created with corresponding rules and be bound to - the subnet of the VM. - Providing `allowed_inbound_ip_addresses` in combination with - `virtual_network_name` is not allowed. - -- `boot_diag_storage_account` (string) - Specify storage to store Boot Diagnostics -- Enabling this option - will create 2 Files in the specified storage account. (serial console log & screehshot file) - once the build is completed, it has to be removed manually. - see [here](https://docs.microsoft.com/en-us/azure/virtual-machines/troubleshooting/boot-diagnostics) for more info - -- `custom_resource_build_prefix` (string) - specify custom azure resource names during build limited to max 10 characters - this will set the prefix for the resources. The actuall resource names will be - `custom_resource_build_prefix` + resourcetype + 5 character random alphanumeric string - -- `async_resourcegroup_delete` (bool) - If you want packer to delete the - temporary resource group asynchronously set this value. It's a boolean - value and defaults to false. Important Setting this true means that - your builds are faster, however any failed deletes are not reported. - - diff --git a/website/content/partials/builder/azure/arm/Config-required.mdx b/website/content/partials/builder/azure/arm/Config-required.mdx deleted file mode 100644 index eb4bd5cc3..000000000 --- a/website/content/partials/builder/azure/arm/Config-required.mdx +++ /dev/null @@ -1,40 +0,0 @@ - - -- `image_publisher` (string) - Name of the publisher to use for your base image (Azure Marketplace Images only). See - [documentation](https://docs.microsoft.com/en-us/cli/azure/vm/image) - for details. - - CLI example `az vm image list-publishers --location westus` - -- `image_offer` (string) - Name of the publisher's offer to use for your base image (Azure Marketplace Images only). See - [documentation](https://docs.microsoft.com/en-us/cli/azure/vm/image) - for details. - - CLI example - `az vm image list-offers --location westus --publisher Canonical` - -- `image_sku` (string) - SKU of the image offer to use for your base image (Azure Marketplace Images only). See - [documentation](https://docs.microsoft.com/en-us/cli/azure/vm/image) - for details. - - CLI example - `az vm image list-skus --location westus --publisher Canonical --offer UbuntuServer` - -- `image_url` (string) - URL to a custom VHD to use for your base image. If this value is set, - image_publisher, image_offer, image_sku, or image_version should not be set. - -- `custom_managed_image_name` (string) - Name of a custom managed image to use for your base image. If this value is set, do - not set image_publisher, image_offer, image_sku, or image_version. - If this value is set, the option - `custom_managed_image_resource_group_name` must also be set. See - [documentation](https://docs.microsoft.com/en-us/azure/storage/storage-managed-disks-overview#images) - to learn more about managed images. - -- `custom_managed_image_resource_group_name` (string) - Name of a custom managed image's resource group to use for your base image. If this - value is set, image_publisher, image_offer, image_sku, or image_version should not be set. - If this value is set, the option - `custom_managed_image_name` must also be set. See - [documentation](https://docs.microsoft.com/en-us/azure/storage/storage-managed-disks-overview#images) - to learn more about managed images. - - diff --git a/website/content/partials/builder/azure/arm/PlanInformation-not-required.mdx b/website/content/partials/builder/azure/arm/PlanInformation-not-required.mdx deleted file mode 100644 index df20d3fa9..000000000 --- a/website/content/partials/builder/azure/arm/PlanInformation-not-required.mdx +++ /dev/null @@ -1,11 +0,0 @@ - - -- `plan_name` (string) - Plan Name - -- `plan_product` (string) - Plan Product - -- `plan_publisher` (string) - Plan Publisher - -- `plan_promotion_code` (string) - Plan Promotion Code - - diff --git a/website/content/partials/builder/azure/arm/SharedImageGallery-not-required.mdx b/website/content/partials/builder/azure/arm/SharedImageGallery-not-required.mdx deleted file mode 100644 index 3d7259951..000000000 --- a/website/content/partials/builder/azure/arm/SharedImageGallery-not-required.mdx +++ /dev/null @@ -1,17 +0,0 @@ - - -- `subscription` (string) - Subscription - -- `resource_group` (string) - Resource Group - -- `gallery_name` (string) - Gallery Name - -- `image_name` (string) - Image Name - -- `image_version` (string) - Specify a specific version of an OS to boot from. - Defaults to latest. There may be a difference in versions available - across regions due to image synchronization latency. To ensure a consistent - version across regions set this value to one that is available in all - regions where you are deploying. - - diff --git a/website/content/partials/builder/azure/arm/SharedImageGalleryDestination-not-required.mdx b/website/content/partials/builder/azure/arm/SharedImageGalleryDestination-not-required.mdx deleted file mode 100644 index 53d413431..000000000 --- a/website/content/partials/builder/azure/arm/SharedImageGalleryDestination-not-required.mdx +++ /dev/null @@ -1,18 +0,0 @@ - - -- `subscription` (string) - Sig Destination Subscription - -- `resource_group` (string) - Sig Destination Resource Group - -- `gallery_name` (string) - Sig Destination Gallery Name - -- `image_name` (string) - Sig Destination Image Name - -- `image_version` (string) - Sig Destination Image Version - -- `replication_regions` ([]string) - Sig Destination Replication Regions - -- `storage_account_type` (string) - Specify a storage account type for the Shared Image Gallery Image Version. - Defaults to `Standard_LRS`. Accepted values are `Standard_LRS` and `Standard_ZRS` - - diff --git a/website/content/partials/builder/azure/chroot/Config-not-required.mdx b/website/content/partials/builder/azure/chroot/Config-not-required.mdx deleted file mode 100644 index 4508bd71e..000000000 --- a/website/content/partials/builder/azure/chroot/Config-not-required.mdx +++ /dev/null @@ -1,66 +0,0 @@ - - -- `from_scratch` (bool) - When set to `true`, starts with an empty, unpartitioned disk. Defaults to `false`. - -- `command_wrapper` (string) - How to run shell commands. This may be useful to set environment variables or perhaps run - a command with sudo or so on. This is a configuration template where the `.Command` variable - is replaced with the command to be run. Defaults to `{{.Command}}`. - -- `pre_mount_commands` ([]string) - A series of commands to execute after attaching the root volume and before mounting the chroot. - This is not required unless using `from_scratch`. If so, this should include any partitioning - and filesystem creation commands. The path to the device is provided by `{{.Device}}`. - -- `mount_options` ([]string) - Options to supply the `mount` command when mounting devices. Each option will be prefixed with - `-o` and supplied to the `mount` command ran by Packer. Because this command is ran in a shell, - user discretion is advised. See this manual page for the `mount` command for valid file system specific options. - -- `mount_partition` (string) - The partition number containing the / partition. By default this is the first partition of the volume. - -- `mount_path` (string) - The path where the volume will be mounted. This is where the chroot environment will be. This defaults - to `/mnt/packer-amazon-chroot-volumes/{{.Device}}`. This is a configuration template where the `.Device` - variable is replaced with the name of the device where the volume is attached. - -- `post_mount_commands` ([]string) - As `pre_mount_commands`, but the commands are executed after mounting the root device and before the - extra mount and copy steps. The device and mount path are provided by `{{.Device}}` and `{{.MountPath}}`. - -- `chroot_mounts` ([][]string) - This is a list of devices to mount into the chroot environment. This configuration parameter requires - some additional documentation which is in the "Chroot Mounts" section below. Please read that section - for more information on how to use this. - -- `copy_files` ([]string) - Paths to files on the running Azure instance that will be copied into the chroot environment prior to - provisioning. Defaults to `/etc/resolv.conf` so that DNS lookups work. Pass an empty list to skip copying - `/etc/resolv.conf`. You may need to do this if you're building an image that uses systemd. - -- `os_disk_size_gb` (int32) - Try to resize the OS disk to this size on the first copy. Disks can only be englarged. If not specified, - the disk will keep its original size. Required when using `from_scratch` - -- `os_disk_storage_account_type` (string) - The [storage SKU](https://docs.microsoft.com/en-us/rest/api/compute/disks/createorupdate#diskstorageaccounttypes) - to use for the OS Disk. Defaults to `Standard_LRS`. - -- `os_disk_cache_type` (string) - The [cache type](https://docs.microsoft.com/en-us/rest/api/compute/images/createorupdate#cachingtypes) - specified in the resulting image and for attaching it to the Packer VM. Defaults to `ReadOnly` - -- `data_disk_storage_account_type` (string) - The [storage SKU](https://docs.microsoft.com/en-us/rest/api/compute/disks/createorupdate#diskstorageaccounttypes) - to use for datadisks. Defaults to `Standard_LRS`. - -- `data_disk_cache_type` (string) - The [cache type](https://docs.microsoft.com/en-us/rest/api/compute/images/createorupdate#cachingtypes) - specified in the resulting image and for attaching it to the Packer VM. Defaults to `ReadOnly` - -- `image_hyperv_generation` (string) - The [Hyper-V generation type](https://docs.microsoft.com/en-us/rest/api/compute/images/createorupdate#hypervgenerationtypes) for Managed Image output. - Defaults to `V1`. - -- `temporary_os_disk_id` (string) - The id of the temporary OS disk that will be created. Will be generated if not set. - -- `temporary_os_disk_snapshot_id` (string) - The id of the temporary OS disk snapshot that will be created. Will be generated if not set. - -- `temporary_data_disk_id_prefix` (string) - The prefix for the resource ids of the temporary data disks that will be created. The disks will be suffixed with a number. Will be generated if not set. - -- `temporary_data_disk_snapshot_id` (string) - The prefix for the resource ids of the temporary data disk snapshots that will be created. The snapshots will be suffixed with a number. Will be generated if not set. - -- `skip_cleanup` (bool) - If set to `true`, leaves the temporary disks and snapshots behind in the Packer VM resource group. Defaults to `false` - -- `image_resource_id` (string) - The managed image to create using this build. - -- `shared_image_destination` (SharedImageGalleryDestination) - The shared image to create using this build. - - diff --git a/website/content/partials/builder/azure/chroot/Config-required.mdx b/website/content/partials/builder/azure/chroot/Config-required.mdx deleted file mode 100644 index 655da4bcd..000000000 --- a/website/content/partials/builder/azure/chroot/Config-required.mdx +++ /dev/null @@ -1,8 +0,0 @@ - - -- `source` (string) - One of the following can be used as a source for an image: - - a shared image version resource ID - - a managed disk resource ID - - a publisher:offer:sku:version specifier for plaform image sources. - - diff --git a/website/content/partials/builder/azure/chroot/Config.mdx b/website/content/partials/builder/azure/chroot/Config.mdx deleted file mode 100644 index e104d62f7..000000000 --- a/website/content/partials/builder/azure/chroot/Config.mdx +++ /dev/null @@ -1,6 +0,0 @@ - - -Config is the configuration that is chained through the steps and settable -from the template. - - diff --git a/website/content/partials/builder/azure/chroot/SharedImageGalleryDestination-not-required.mdx b/website/content/partials/builder/azure/chroot/SharedImageGalleryDestination-not-required.mdx deleted file mode 100644 index 4a90d461f..000000000 --- a/website/content/partials/builder/azure/chroot/SharedImageGalleryDestination-not-required.mdx +++ /dev/null @@ -1,7 +0,0 @@ - - -- `target_regions` ([]TargetRegion) - Target Regions - -- `exclude_from_latest` (bool) - Exclude From Latest - - diff --git a/website/content/partials/builder/azure/chroot/SharedImageGalleryDestination-required.mdx b/website/content/partials/builder/azure/chroot/SharedImageGalleryDestination-required.mdx deleted file mode 100644 index 7ec91ff39..000000000 --- a/website/content/partials/builder/azure/chroot/SharedImageGalleryDestination-required.mdx +++ /dev/null @@ -1,11 +0,0 @@ - - -- `resource_group` (string) - Resource Group - -- `gallery_name` (string) - Gallery Name - -- `image_name` (string) - Image Name - -- `image_version` (string) - Image Version - - diff --git a/website/content/partials/builder/azure/chroot/SharedImageGalleryDestination.mdx b/website/content/partials/builder/azure/chroot/SharedImageGalleryDestination.mdx deleted file mode 100644 index 565c5eda1..000000000 --- a/website/content/partials/builder/azure/chroot/SharedImageGalleryDestination.mdx +++ /dev/null @@ -1,6 +0,0 @@ - - -SharedImageGalleryDestination models an image version in a Shared -Image Gallery that can be used as a destination. - - diff --git a/website/content/partials/builder/azure/chroot/TargetRegion-not-required.mdx b/website/content/partials/builder/azure/chroot/TargetRegion-not-required.mdx deleted file mode 100644 index d277b7f54..000000000 --- a/website/content/partials/builder/azure/chroot/TargetRegion-not-required.mdx +++ /dev/null @@ -1,7 +0,0 @@ - - -- `replicas` (int32) - Number of replicas in this region. Default: 1 - -- `storage_account_type` (string) - Storage account type: Standard_LRS or Standard_ZRS. Default: Standard_ZRS - - diff --git a/website/content/partials/builder/azure/chroot/TargetRegion-required.mdx b/website/content/partials/builder/azure/chroot/TargetRegion-required.mdx deleted file mode 100644 index c76629422..000000000 --- a/website/content/partials/builder/azure/chroot/TargetRegion-required.mdx +++ /dev/null @@ -1,5 +0,0 @@ - - -- `name` (string) - Name of the Azure region - - diff --git a/website/content/partials/builder/azure/chroot/TargetRegion.mdx b/website/content/partials/builder/azure/chroot/TargetRegion.mdx deleted file mode 100644 index 28ac589e2..000000000 --- a/website/content/partials/builder/azure/chroot/TargetRegion.mdx +++ /dev/null @@ -1,5 +0,0 @@ - - -TargetRegion describes a region where the shared image should be replicated - - diff --git a/website/content/partials/builder/azure/common/client/Config-not-required.mdx b/website/content/partials/builder/azure/common/client/Config-not-required.mdx deleted file mode 100644 index 85f3489a5..000000000 --- a/website/content/partials/builder/azure/common/client/Config-not-required.mdx +++ /dev/null @@ -1,36 +0,0 @@ - - -- `cloud_environment_name` (string) - One of Public, China, Germany, or - USGovernment. Defaults to Public. Long forms such as - USGovernmentCloud and AzureUSGovernmentCloud are also supported. - -- `client_id` (string) - The application ID of the AAD Service Principal. - Requires either `client_secret`, `client_cert_path` or `client_jwt` to be set as well. - -- `client_secret` (string) - A password/secret registered for the AAD SP. - -- `client_cert_path` (string) - The path to a pem-encoded certificate that will be used to authenticate - as the specified AAD SP. - -- `client_cert_token_timeout` (duration string | ex: "1h5m2s") - The timeout for the JWT Token when using a [client certificate](#client_cert_path). Defaults to 1 hour. - -- `client_jwt` (string) - A JWT bearer token for client auth (RFC 7523, Sec. 2.2) that will be used - to authenticate the AAD SP. Provides more control over token the expiration - when using certificate authentication than when using `client_cert_path`. - -- `object_id` (string) - The object ID for the AAD SP. Optional, will be derived from the oAuth token if left empty. - -- `tenant_id` (string) - The Active Directory tenant identifier with which your `client_id` and - `subscription_id` are associated. If not specified, `tenant_id` will be - looked up using `subscription_id`. - -- `subscription_id` (string) - The subscription to use. - -- `use_azure_cli_auth` (bool) - Flag to use Azure CLI authentication. Defaults to false. - CLI auth will use the information from an active `az login` session to connect to Azure and set the subscription id and tenant id associated to the signed in account. - If enabled, it will use the authentication provided by the `az` CLI. - Azure CLI authentication will use the credential marked as `isDefault` and can be verified using `az account show`. - Works with normal authentication (`az login`) and service principals (`az login --service-principal --username APP_ID --password PASSWORD --tenant TENANT_ID`). - Ignores all other configurations if enabled. - - diff --git a/website/content/partials/builder/azure/common/client/Config.mdx b/website/content/partials/builder/azure/common/client/Config.mdx deleted file mode 100644 index 16806af81..000000000 --- a/website/content/partials/builder/azure/common/client/Config.mdx +++ /dev/null @@ -1,12 +0,0 @@ - - -Config allows for various ways to authenticate Azure clients. -When `client_id` and `subscription_id` are specified, Packer will use the -specified Azure Active Directory (AAD) Service Principal (SP). -If only `subscription_id` is specified, Packer will try to interactively -log on the current user (tokens will be cached). -If none of these options are specified, Packer will attempt to use the -Managed Identity and subscription of the VM that Packer is running on. -This will only work if Packer is running on an Azure VM. - - diff --git a/website/content/partials/builder/azure/dtl/ArtifactParameter-not-required.mdx b/website/content/partials/builder/azure/dtl/ArtifactParameter-not-required.mdx deleted file mode 100644 index fc507c1bd..000000000 --- a/website/content/partials/builder/azure/dtl/ArtifactParameter-not-required.mdx +++ /dev/null @@ -1,9 +0,0 @@ - - -- `name` (string) - Name - -- `value` (string) - Value - -- `type` (string) - Type - - diff --git a/website/content/partials/builder/azure/dtl/Config-not-required.mdx b/website/content/partials/builder/azure/dtl/Config-not-required.mdx deleted file mode 100644 index afb160163..000000000 --- a/website/content/partials/builder/azure/dtl/Config-not-required.mdx +++ /dev/null @@ -1,169 +0,0 @@ - - -- `capture_name_prefix` (string) - Capture - -- `capture_container_name` (string) - Capture Container Name - -- `shared_image_gallery` (SharedImageGallery) - Use a [Shared Gallery - image](https://azure.microsoft.com/en-us/blog/announcing-the-public-preview-of-shared-image-gallery/) - as the source for this build. *VHD targets are incompatible with this - build type* - the target must be a *Managed Image*. - - ```json - "shared_image_gallery": { - "subscription": "00000000-0000-0000-0000-00000000000", - "resource_group": "ResourceGroup", - "gallery_name": "GalleryName", - "image_name": "ImageName", - "image_version": "1.0.0" - } - "managed_image_name": "TargetImageName", - "managed_image_resource_group_name": "TargetResourceGroup" - ``` - -- `shared_image_gallery_destination` (SharedImageGalleryDestination) - The name of the Shared Image Gallery under which the managed image will be published as Shared Gallery Image version. - - Following is an example. - - ```json - "shared_image_gallery_destination": { - "resource_group": "ResourceGroup", - "gallery_name": "GalleryName", - "image_name": "ImageName", - "image_version": "1.0.0", - "replication_regions": ["regionA", "regionB", "regionC"] - } - "managed_image_name": "TargetImageName", - "managed_image_resource_group_name": "TargetResourceGroup" - ``` - -- `shared_image_gallery_timeout` (duration string | ex: "1h5m2s") - How long to wait for an image to be published to the shared image - gallery before timing out. If your Packer build is failing on the - Publishing to Shared Image Gallery step with the error `Original Error: - context deadline exceeded`, but the image is present when you check your - Azure dashboard, then you probably need to increase this timeout from - its default of "60m" (valid time units include `s` for seconds, `m` for - minutes, and `h` for hours.) - -- `image_publisher` (string) - PublisherName for your base image. See - [documentation](https://docs.microsoft.com/en-us/cli/azure/vm/image) - for details. - - CLI example `az vm image list-publishers --location westus` - -- `image_offer` (string) - Offer for your base image. See - [documentation](https://docs.microsoft.com/en-us/cli/azure/vm/image) - for details. - - CLI example - `az vm image list-offers --location westus --publisher Canonical` - -- `image_sku` (string) - SKU for your base image. See - [documentation](https://docs.microsoft.com/en-us/cli/azure/vm/image) - for details. - - CLI example - `az vm image list-skus --location westus --publisher Canonical --offer UbuntuServer` - -- `image_version` (string) - Specify a specific version of an OS to boot from. - Defaults to `latest`. There may be a difference in versions available - across regions due to image synchronization latency. To ensure a consistent - version across regions set this value to one that is available in all - regions where you are deploying. - - CLI example - `az vm image list --location westus --publisher Canonical --offer UbuntuServer --sku 16.04.0-LTS --all` - -- `image_url` (string) - Specify a custom VHD to use. If this value is set, do - not set image_publisher, image_offer, image_sku, or image_version. - -- `custom_managed_image_resource_group_name` (string) - Specify the source managed image's resource group used to use. If this - value is set, do not set image\_publisher, image\_offer, image\_sku, or - image\_version. If this value is set, the value - `custom_managed_image_name` must also be set. See - [documentation](https://docs.microsoft.com/en-us/azure/storage/storage-managed-disks-overview#images) - to learn more about managed images. - -- `custom_managed_image_name` (string) - Specify the source managed image's name to use. If this value is set, do - not set image\_publisher, image\_offer, image\_sku, or image\_version. - If this value is set, the value - `custom_managed_image_resource_group_name` must also be set. See - [documentation](https://docs.microsoft.com/en-us/azure/storage/storage-managed-disks-overview#images) - to learn more about managed images. - -- `location` (string) - Location - -- `vm_size` (string) - Size of the VM used for building. This can be changed when you deploy a - VM from your VHD. See - [pricing](https://azure.microsoft.com/en-us/pricing/details/virtual-machines/) - information. Defaults to `Standard_A1`. - - CLI example `az vm list-sizes --location westus` - -- `managed_image_resource_group_name` (string) - Specify the managed image resource group name where the result of the - Packer build will be saved. The resource group must already exist. If - this value is set, the value managed_image_name must also be set. See - documentation to learn more about managed images. - -- `managed_image_name` (string) - Specify the managed image name where the result of the Packer build will - be saved. The image name must not exist ahead of time, and will not be - overwritten. If this value is set, the value - managed_image_resource_group_name must also be set. See documentation to - learn more about managed images. - -- `managed_image_storage_account_type` (string) - Specify the storage account - type for a managed image. Valid values are Standard_LRS and Premium_LRS. - The default is Standard_LRS. - -- `azure_tags` (map[string]\*string) - the user can define up to 15 - tags. Tag names cannot exceed 512 characters, and tag values cannot exceed - 256 characters. Tags are applied to every resource deployed by a Packer - build, i.e. Resource Group, VM, NIC, VNET, Public IP, KeyVault, etc. - -- `plan_id` (string) - Used for creating images from Marketplace images. Please refer to - [Deploy an image with Marketplace - terms](https://aka.ms/azuremarketplaceapideployment) for more details. - Not all Marketplace images support programmatic deployment, and support - is controlled by the image publisher. - Plan_id is a string with unique identifier for the plan associated with images. - Ex plan_id="1-12ab" - -- `polling_duration_timeout` (duration string | ex: "1h5m2s") - The default PollingDuration for azure is 15mins, this property will override - that value. See [Azure DefaultPollingDuration](https://godoc.org/github.com/Azure/go-autorest/autorest#pkg-constants) - If your Packer build is failing on the - ARM deployment step with the error `Original Error: - context deadline exceeded`, then you probably need to increase this timeout from - its default of "15m" (valid time units include `s` for seconds, `m` for - minutes, and `h` for hours.) - -- `os_type` (string) - If either Linux or Windows is specified Packer will - automatically configure authentication credentials for the provisioned - machine. For Linux this configures an SSH authorized key. For Windows - this configures a WinRM certificate. - -- `os_disk_size_gb` (int32) - Specify the size of the OS disk in GB - (gigabytes). Values of zero or less than zero are ignored. - -- `disk_additional_size` ([]int32) - For Managed build the final artifacts are included in the managed image. - The additional disk will have the same storage account type as the OS - disk, as specified with the `managed_image_storage_account_type` - setting. - -- `disk_caching_type` (string) - Specify the disk caching type. Valid values - are None, ReadOnly, and ReadWrite. The default value is ReadWrite. - -- `storage_type` (string) - DTL values - -- `lab_virtual_network_name` (string) - Lab Virtual Network Name - -- `lab_name` (string) - Lab Name - -- `lab_subnet_name` (string) - Lab Subnet Name - -- `lab_resource_group_name` (string) - Lab Resource Group Name - -- `dtl_artifacts` ([]DtlArtifact) - Dtl Artifacts - -- `vm_name` (string) - VM Name - - diff --git a/website/content/partials/builder/azure/dtl/DtlArtifact-not-required.mdx b/website/content/partials/builder/azure/dtl/DtlArtifact-not-required.mdx deleted file mode 100644 index 4df27fc30..000000000 --- a/website/content/partials/builder/azure/dtl/DtlArtifact-not-required.mdx +++ /dev/null @@ -1,11 +0,0 @@ - - -- `artifact_name` (string) - Artifact Name - -- `repository_name` (string) - Repository Name - -- `artifact_id` (string) - Artifact Id - -- `parameters` ([]ArtifactParameter) - Parameters - - diff --git a/website/content/partials/builder/azure/dtl/SharedImageGallery-not-required.mdx b/website/content/partials/builder/azure/dtl/SharedImageGallery-not-required.mdx deleted file mode 100644 index 7869d459a..000000000 --- a/website/content/partials/builder/azure/dtl/SharedImageGallery-not-required.mdx +++ /dev/null @@ -1,13 +0,0 @@ - - -- `subscription` (string) - Subscription - -- `resource_group` (string) - Resource Group - -- `gallery_name` (string) - Gallery Name - -- `image_name` (string) - Image Name - -- `image_version` (string) - Image Version - - diff --git a/website/content/partials/builder/azure/dtl/SharedImageGalleryDestination-not-required.mdx b/website/content/partials/builder/azure/dtl/SharedImageGalleryDestination-not-required.mdx deleted file mode 100644 index 6cd1f91a5..000000000 --- a/website/content/partials/builder/azure/dtl/SharedImageGalleryDestination-not-required.mdx +++ /dev/null @@ -1,13 +0,0 @@ - - -- `resource_group` (string) - Sig Destination Resource Group - -- `gallery_name` (string) - Sig Destination Gallery Name - -- `image_name` (string) - Sig Destination Image Name - -- `image_version` (string) - Sig Destination Image Version - -- `replication_regions` ([]string) - Sig Destination Replication Regions - - diff --git a/website/data/docs-nav-data.json b/website/data/docs-nav-data.json index 8f11a96a2..bc372b0b4 100644 --- a/website/data/docs-nav-data.json +++ b/website/data/docs-nav-data.json @@ -679,23 +679,6 @@ "title": "Overview", "path": "builders" }, - { - "title": "Azure", - "routes": [ - { - "title": "Overview", - "path": "builders/azure" - }, - { - "title": "ARM", - "path": "builders/azure/arm" - }, - { - "title": "chroot", - "path": "builders/azure/chroot" - } - ] - }, { "title": "File", "path": "builders/file" diff --git a/website/data/docs-remote-plugins.json b/website/data/docs-remote-plugins.json index 45a0a687a..d5e2b0827 100644 --- a/website/data/docs-remote-plugins.json +++ b/website/data/docs-remote-plugins.json @@ -18,6 +18,12 @@ "repo": "hashicorp/packer-plugin-amazon", "version": "latest" }, + { + "title": "Azure", + "path": "azure", + "repo": "hashicorp/packer-plugin-azure", + "version": "latest" + }, { "title": "Chef", "path": "chef",