From a4471f896931fcecbc83ef1a8ed91d242afc31cf Mon Sep 17 00:00:00 2001 From: Jenna Goldstrich Date: Fri, 24 Mar 2023 14:07:55 -0700 Subject: [PATCH 01/15] ARM Builder Rebasing entire ARM builder migration on top of main Remove some missed old SDK stuff, including the autorest To package from ARM Builder Update oidc-example.json Pass SIG ID subscription through to state Use Hamilton MSGraph SDK to fetch Object ID for Windows builds Format and add comment commit generated code Switch AuthType back to private variable and use helper function to get it, switch from MeClient to ServicePrincipals client to get objectID Print MSgraph error again Don't use Hamilton, get it from the Access Token fmt and pass in correct context on cleanup Mess with backoff time while testing to see if this can be a bit smoother of a process for users Don't use context.TODOs in ARM Builder client calls Use hcl file in oidc test Use Giovanni instead of Blob Container serivce Implement new SDK environment logic Address fmt Remove unused function, go mod tidy Switch to forked release process --- .github/workflows/release.yml | 9 +- .github/workflows/wip-test-oidc.yml | 55 +++ .goreleaser.yml | 15 +- CODEOWNERS | 2 +- builder/azure/arm/artifact.go | 30 +- builder/azure/arm/artifact_test.go | 60 +-- builder/azure/arm/azure_client.go | 416 +++++++++++------- builder/azure/arm/builder.go | 140 +++--- builder/azure/arm/builder_acc_test.go | 110 ++--- builder/azure/arm/builder_test.go | 2 +- builder/azure/arm/config.go | 68 +-- builder/azure/arm/config.hcl2spec.go | 111 ++++- builder/azure/arm/config_test.go | 20 +- builder/azure/arm/resource_resolver.go | 47 +- builder/azure/arm/resource_resolver_test.go | 4 +- builder/azure/arm/step_capture_image.go | 106 ++--- builder/azure/arm/step_capture_image_test.go | 42 +- .../azure/arm/step_certificate_in_keyvault.go | 25 +- .../arm/step_certificate_in_keyvault_test.go | 44 +- .../azure/arm/step_create_resource_group.go | 74 ++-- .../arm/step_create_resource_group_test.go | 65 +-- builder/azure/arm/step_deploy_template.go | 162 +++---- .../azure/arm/step_deploy_template_test.go | 29 +- .../azure/arm/step_get_additional_disks.go | 30 +- .../arm/step_get_additional_disks_test.go | 41 +- builder/azure/arm/step_get_certificate.go | 19 +- .../azure/arm/step_get_certificate_test.go | 11 +- builder/azure/arm/step_get_ip_address.go | 31 +- builder/azure/arm/step_get_ip_address_test.go | 15 +- builder/azure/arm/step_get_os_disk.go | 26 +- builder/azure/arm/step_get_os_disk_test.go | 34 +- .../azure/arm/step_get_source_image_name.go | 29 +- .../arm/step_get_source_image_name_test.go | 40 +- builder/azure/arm/step_power_off_compute.go | 14 +- .../azure/arm/step_power_off_compute_test.go | 15 +- .../step_publish_to_shared_image_gallery.go | 85 ++-- ...ep_publish_to_shared_image_gallery_test.go | 21 +- builder/azure/arm/step_snapshot_data_disks.go | 44 +- .../arm/step_snapshot_data_disks_test.go | 11 +- builder/azure/arm/step_snapshot_os_disk.go | 43 +- .../azure/arm/step_snapshot_os_disk_test.go | 11 +- builder/azure/arm/step_validate_template.go | 12 +- .../azure/arm/step_validate_template_test.go | 17 +- builder/azure/arm/template_factory.go | 31 +- ...stVirtualMachineDeployment13.approved.json | 2 +- builder/azure/arm/template_factory_test.go | 10 +- builder/azure/common/client/config.go | 120 +++-- builder/azure/common/client/config_test.go | 20 +- .../azure/common/client/tokenprovider_cli.go | 63 +++ builder/azure/common/constants/stateBag.go | 46 +- builder/azure/common/pointer.go | 36 ++ builder/azure/common/template/template.go | 113 ++--- .../azure/common/template/template_builder.go | 197 +++++---- ...st.TestBuildEncryptedWindows.approved.json | 2 +- ...lder_test.TestBuildWindows00.approved.json | 2 +- ...lder_test.TestBuildWindows01.approved.json | 2 +- ...lder_test.TestBuildWindows02.approved.json | 2 +- .../common/template/template_builder_test.go | 2 +- .../builder/azure/arm/Spot-not-required.mdx | 2 +- example/oidc-example.json | 28 ++ example/oidc-example.pkr.hcl | 39 ++ go.mod | 77 ++-- go.sum | 185 +++++--- 63 files changed, 1894 insertions(+), 1270 deletions(-) create mode 100644 .github/workflows/wip-test-oidc.yml create mode 100644 builder/azure/common/pointer.go create mode 100644 example/oidc-example.json create mode 100644 example/oidc-example.pkr.hcl diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 8c0734f4..5d807353 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -48,8 +48,12 @@ jobs: - name: Describe plugin id: plugin_describe run: echo "api_version=$(go run . describe | jq -r '.api_version')" >> "$GITHUB_OUTPUT" - - name: Install signore - uses: hashicorp/setup-signore-package@v1 + - name: Import GPG key + id: import_gpg + uses: crazy-max/ghaction-import-gpg@v5 + with: + gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }} + passphrase: ${{ secrets.GPG_PASSPHRASE }} - name: Run GoReleaser uses: goreleaser/goreleaser-action@f82d6c1c344bcacabba2c841718984797f664a6b # v4.2.0 with: @@ -58,6 +62,7 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} API_VERSION: ${{ steps.plugin_describe.outputs.api_version }} + GPG_FINGERPRINT: ${{ steps.import_gpg.outputs.fingerprint }} SIGNORE_CLIENT_ID: ${{ secrets.SIGNORE_CLIENT_ID }} SIGNORE_CLIENT_SECRET: ${{ secrets.SIGNORE_CLIENT_SECRET }} SIGNORE_SIGNER: ${{ secrets.SIGNORE_SIGNER }} diff --git a/.github/workflows/wip-test-oidc.yml b/.github/workflows/wip-test-oidc.yml new file mode 100644 index 00000000..9cc95874 --- /dev/null +++ b/.github/workflows/wip-test-oidc.yml @@ -0,0 +1,55 @@ +--- +# taken and modified from https://github.com/hashicorp/go-azure-sdk/blob/main/.github/workflows/pr-acceptance-tests.yml +name: OIDC Example - Testing OIDC integration in the SDK branch +on: + push: + +permissions: + contents: read + id-token: write + +jobs: + secrets-check: + runs-on: ubuntu-latest + outputs: + available: "${{ steps.check-secrets.outputs.available }}" + steps: + # we check for the ACTIONS_ID_TOKEN_REQUEST_URL variable as a proxy for other secrets + # it will be unset when running for a PR from a fork + - id: check-secrets + run: | + if [[ "${ACTIONS_ID_TOKEN_REQUEST_URL}" == "" ]]; then + echo "available=false" | tee ${GITHUB_OUTPUT} + else + echo "available=true" | tee ${GITHUB_OUTPUT} + fi + + test-oidc: + runs-on: ubuntu-latest + needs: [secrets-check] + if: needs.secrets-check.outputs.available == 'true' + steps: + - name: Set OIDC Token + run: | + echo "ARM_OIDC_TOKEN=$(curl -H "Accept: application/json; api-version=2.0" -H "Authorization: Bearer ${ACTIONS_ID_TOKEN_REQUEST_TOKEN}" -H "Content-Type: application/json" -G --data-urlencode "audience=api://AzureADTokenExchange" "${ACTIONS_ID_TOKEN_REQUEST_URL}" | jq -r '.value')" >>${GITHUB_ENV} + + - name: Install Go + uses: actions/setup-go@4d34df0c2316fe8122ab82dc22947d607c0c91f9 # v4.0.0 + with: + go-version: '1.19.5' + + - name: Checkout + uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 + + - name: Setup `packer` + uses: hashicorp/setup-packer@main + id: setup + + - name: Build the plugin + run: make + + - name: Try to run an AzureARM build with our OIDC token + run: packer build -force ./example/oidc-example.pkr.hcl + env: + ARM_CLIENT_ID: ${{ secrets.ARM_CLIENT_ID}} + ARM_SUBSCRIPTION_ID: ${{ secrets.ARM_SUBSCRIPTION_ID}} diff --git a/.goreleaser.yml b/.goreleaser.yml index 1c48ac23..0d2606bc 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -94,10 +94,17 @@ checksum: name_template: '{{ .ProjectName }}_v{{ .Version }}_SHA256SUMS' algorithm: sha256 signs: - - cmd: signore - args: ["sign", "--dearmor", "--file", "${artifact}", "--out", "${signature}"] - artifacts: checksum - signature: ${artifact}.sig + - artifacts: checksum + args: + # if you are using this is in a GitHub action or some other automated pipeline, you + # need to pass the batch flag to indicate its not interactive. + - "--batch" + - "--local-user" + - "{{ .Env.GPG_FINGERPRINT }}" + - "--output" + - "${signature}" + - "--detach-sign" + - "${artifact}" release: # If you want to manually examine the release before its live, uncomment this line: # draft: true diff --git a/CODEOWNERS b/CODEOWNERS index e2487081..8847bfdc 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -1 +1 @@ -* @hashicorp/packer +* @JenGoldstrich @nywilken @lbajolet-hashicorp diff --git a/builder/azure/arm/artifact.go b/builder/azure/arm/artifact.go index 13159375..d022942e 100644 --- a/builder/azure/arm/artifact.go +++ b/builder/azure/arm/artifact.go @@ -20,8 +20,7 @@ const ( ) type AdditionalDiskArtifact struct { - AdditionalDiskUri string - AdditionalDiskUriReadOnlySas string + AdditionalDiskUri string } type Artifact struct { @@ -32,8 +31,6 @@ type Artifact struct { StorageAccountLocation string OSDiskUri string TemplateUri string - OSDiskUriReadOnlySas string - TemplateUriReadOnlySas string // Managed Image ManagedImageResourceGroupName string @@ -56,7 +53,7 @@ type Artifact struct { 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) { +func NewManagedImageArtifact(osType, resourceGroup, name, location, id, osDiskSnapshotName, dataDiskSnapshotPrefix string, generatedData map[string]interface{}, keepOSDisk bool, template *CaptureTemplate) (*Artifact, error) { res := Artifact{ ManagedImageResourceGroupName: resourceGroup, ManagedImageName: name, @@ -86,7 +83,6 @@ func NewManagedImageArtifact(osType, resourceGroup, name, location, id, osDiskSn } res.OSDiskUri = vhdUri.String() - res.OSDiskUriReadOnlySas = getSasUrl(getStorageUrlPath(vhdUri)) } return &res, nil @@ -115,7 +111,7 @@ func NewSharedImageArtifact(osType, destinationSharedImageGalleryId string, loca }, nil } -func NewArtifact(template *CaptureTemplate, getSasUrl func(name string) string, osType string, generatedData map[string]interface{}) (*Artifact, error) { +func NewArtifact(template *CaptureTemplate, osType string, generatedData map[string]interface{}) (*Artifact, error) { if template == nil { return nil, fmt.Errorf("nil capture template") } @@ -143,17 +139,14 @@ func NewArtifact(template *CaptureTemplate, getSasUrl func(name string) string, 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)), + OSType: osType, + OSDiskUri: vhdUri.String(), + TemplateUri: templateUri.String(), AdditionalDisks: additional_disks, @@ -163,11 +156,6 @@ func NewArtifact(template *CaptureTemplate, getSasUrl func(name string) string, }, 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) @@ -249,19 +237,13 @@ func (a *Artifact) String() string { 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 if !a.isPublishedToSIG() { 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)) } } } diff --git a/builder/azure/arm/artifact_test.go b/builder/azure/arm/artifact_test.go index c01dce85..e1582abe 100644 --- a/builder/azure/arm/artifact_test.go +++ b/builder/azure/arm/artifact_test.go @@ -4,7 +4,6 @@ package arm import ( - "fmt" "strings" "testing" @@ -13,10 +12,6 @@ import ( "github.com/mitchellh/mapstructure" ) -func getFakeSasUrl(name string) string { - return fmt.Sprintf("SAS-%s", name) -} - func generatedData() map[string]interface{} { return make(map[string]interface{}) } @@ -39,7 +34,7 @@ func TestArtifactIdVHD(t *testing.T) { }, } - artifact, err := NewArtifact(&template, getFakeSasUrl, "Linux", generatedData()) + artifact, err := NewArtifact(&template, "Linux", generatedData()) if err != nil { t.Fatalf("err=%s", err) } @@ -70,7 +65,7 @@ func TestArtifactIDManagedImage(t *testing.T) { }, } - artifact, err := NewManagedImageArtifact("Linux", "fakeResourceGroup", "fakeName", "fakeLocation", "fakeID", "fakeOsDiskSnapshotName", "fakeDataDiskSnapshotPrefix", generatedData(), false, &template, getFakeSasUrl) + artifact, err := NewManagedImageArtifact("Linux", "fakeResourceGroup", "fakeName", "fakeLocation", "fakeID", "fakeOsDiskSnapshotName", "fakeDataDiskSnapshotPrefix", generatedData(), false, &template) if err != nil { t.Fatalf("err=%s", err) } @@ -110,7 +105,7 @@ func TestArtifactIDManagedImageWithoutOSDiskSnapshotName(t *testing.T) { }, } - artifact, err := NewManagedImageArtifact("Linux", "fakeResourceGroup", "fakeName", "fakeLocation", "fakeID", "", "fakeDataDiskSnapshotPrefix", generatedData(), false, &template, getFakeSasUrl) + artifact, err := NewManagedImageArtifact("Linux", "fakeResourceGroup", "fakeName", "fakeLocation", "fakeID", "", "fakeDataDiskSnapshotPrefix", generatedData(), false, &template) if err != nil { t.Fatalf("err=%s", err) } @@ -149,7 +144,7 @@ func TestArtifactIDManagedImageWithoutDataDiskSnapshotPrefix(t *testing.T) { }, } - artifact, err := NewManagedImageArtifact("Linux", "fakeResourceGroup", "fakeName", "fakeLocation", "fakeID", "fakeOsDiskSnapshotName", "", generatedData(), false, &template, getFakeSasUrl) + artifact, err := NewManagedImageArtifact("Linux", "fakeResourceGroup", "fakeName", "fakeLocation", "fakeID", "fakeOsDiskSnapshotName", "", generatedData(), false, &template) if err != nil { t.Fatalf("err=%s", err) } @@ -188,7 +183,7 @@ func TestArtifactIDManagedImageWithKeepingTheOSDisk(t *testing.T) { }, } - artifact, err := NewManagedImageArtifact("Linux", "fakeResourceGroup", "fakeName", "fakeLocation", "fakeID", "fakeOsDiskSnapshotName", "", generatedData(), true, &template, getFakeSasUrl) + artifact, err := NewManagedImageArtifact("Linux", "fakeResourceGroup", "fakeName", "fakeLocation", "fakeID", "fakeOsDiskSnapshotName", "", generatedData(), true, &template) if err != nil { t.Fatalf("err=%s", err) } @@ -202,7 +197,6 @@ 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() @@ -414,7 +408,7 @@ func TestArtifactString(t *testing.T) { }, } - artifact, err := NewArtifact(&template, getFakeSasUrl, "Linux", generatedData()) + artifact, err := NewArtifact(&template, "Linux", generatedData()) if err != nil { t.Fatalf("err=%s", err) } @@ -423,15 +417,9 @@ func TestArtifactString(t *testing.T) { 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") } @@ -465,7 +453,7 @@ func TestAdditionalDiskArtifactString(t *testing.T) { }, } - artifact, err := NewArtifact(&template, getFakeSasUrl, "Linux", generatedData()) + artifact, err := NewArtifact(&template, "Linux", generatedData()) if err != nil { t.Fatalf("err=%s", err) } @@ -474,15 +462,9 @@ func TestAdditionalDiskArtifactString(t *testing.T) { 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") } @@ -492,9 +474,6 @@ func TestAdditionalDiskArtifactString(t *testing.T) { 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) { @@ -515,7 +494,7 @@ func TestArtifactProperties(t *testing.T) { }, } - testSubject, err := NewArtifact(&template, getFakeSasUrl, "Linux", generatedData()) + testSubject, err := NewArtifact(&template, "Linux", generatedData()) if err != nil { t.Fatalf("err=%s", err) } @@ -523,15 +502,9 @@ func TestArtifactProperties(t *testing.T) { 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) } @@ -565,7 +538,7 @@ func TestAdditionalDiskArtifactProperties(t *testing.T) { }, } - testSubject, err := NewArtifact(&template, getFakeSasUrl, "Linux", generatedData()) + testSubject, err := NewArtifact(&template, "Linux", generatedData()) if err != nil { t.Fatalf("err=%s", err) } @@ -573,15 +546,9 @@ func TestAdditionalDiskArtifactProperties(t *testing.T) { 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) } @@ -597,9 +564,6 @@ func TestAdditionalDiskArtifactProperties(t *testing.T) { 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) { @@ -620,7 +584,7 @@ func TestArtifactOverHyphenatedCaptureUri(t *testing.T) { }, } - testSubject, err := NewArtifact(&template, getFakeSasUrl, "Linux", generatedData()) + testSubject, err := NewArtifact(&template, "Linux", generatedData()) if err != nil { t.Fatalf("err=%s", err) } @@ -633,7 +597,7 @@ func TestArtifactOverHyphenatedCaptureUri(t *testing.T) { func TestArtifactRejectMalformedTemplates(t *testing.T) { template := CaptureTemplate{} - _, err := NewArtifact(&template, getFakeSasUrl, "Linux", generatedData()) + _, err := NewArtifact(&template, "Linux", generatedData()) if err == nil { t.Fatalf("Expected artifact creation to fail, but it succeeded.") } @@ -656,7 +620,7 @@ func TestArtifactRejectMalformedStorageUri(t *testing.T) { }, } - _, err := NewArtifact(&template, getFakeSasUrl, "Linux", generatedData()) + _, err := NewArtifact(&template, "Linux", generatedData()) if err == nil { t.Fatalf("Expected artifact creation to fail, but it succeeded.") } diff --git a/builder/azure/arm/azure_client.go b/builder/azure/arm/azure_client.go index a80442b4..f5833add 100644 --- a/builder/azure/arm/azure_client.go +++ b/builder/azure/arm/azure_client.go @@ -8,24 +8,34 @@ import ( "encoding/json" "fmt" "math" - "net/http" - "net/url" "os" "strconv" "time" - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2021-11-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" + "net/http" + "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-azure/builder/azure/common" + "github.com/golang-jwt/jwt" + hashiImagesSDK "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-01/images" + hashiVMSDK "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-01/virtualmachines" + hashiDisksSDK "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-02/disks" + hashiSnapshotsSDK "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-02/snapshots" + hashiGalleryImagesSDK "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-03/galleryimages" + hashiGalleryImageVersionsSDK "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-03/galleryimageversions" + hashiSecretsSDK "github.com/hashicorp/go-azure-sdk/resource-manager/keyvault/2023-02-01/secrets" + hashiVaultsSDK "github.com/hashicorp/go-azure-sdk/resource-manager/keyvault/2023-02-01/vaults" + hashiNetworkMetaSDK "github.com/hashicorp/go-azure-sdk/resource-manager/network/2022-09-01" + hashiDeploymentOperationsSDK "github.com/hashicorp/go-azure-sdk/resource-manager/resources/2022-09-01/deploymentoperations" + hashiDeploymentsSDK "github.com/hashicorp/go-azure-sdk/resource-manager/resources/2022-09-01/deployments" + hashiGroupsSDK "github.com/hashicorp/go-azure-sdk/resource-manager/resources/2022-09-01/resourcegroups" + hashiStorageAccountsSDK "github.com/hashicorp/go-azure-sdk/resource-manager/storage/2022-09-01/storageaccounts" + "github.com/hashicorp/go-azure-sdk/sdk/auth" + authWrapper "github.com/hashicorp/go-azure-sdk/sdk/auth/autorest" + "github.com/hashicorp/go-azure-sdk/sdk/client/resourcemanager" + "github.com/hashicorp/go-azure-sdk/sdk/environments" "github.com/hashicorp/packer-plugin-azure/version" "github.com/hashicorp/packer-plugin-sdk/useragent" + giovanniBlobStorageSDK "github.com/tombuildsstuff/giovanni/storage/2020-08-04/blob/blobs" ) const ( @@ -33,28 +43,23 @@ const ( ) 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 - compute.GalleryImageVersionsClient - compute.GalleryImagesClient - + NetworkMetaClient hashiNetworkMetaSDK.Client + hashiDeploymentsSDK.DeploymentsClient + hashiStorageAccountsSDK.StorageAccountsClient + hashiDeploymentOperationsSDK.DeploymentOperationsClient + hashiImagesSDK.ImagesClient + hashiVMSDK.VirtualMachinesClient + hashiSecretsSDK.SecretsClient + hashiVaultsSDK.VaultsClient + hashiDisksSDK.DisksClient + hashiGroupsSDK.ResourceGroupsClient + hashiSnapshotsSDK.SnapshotsClient + hashiGalleryImageVersionsSDK.GalleryImageVersionsClient + hashiGalleryImagesSDK.GalleryImagesClient + GiovanniBlobClient giovanniBlobStorageSDK.Client InspectorMaxLength int Template *CaptureTemplate LastError azureErrorResponse - VaultClientDelete keyvault.VaultsClient } func getCaptureResponse(body string) *CaptureTemplate { @@ -131,167 +136,152 @@ func byConcatDecorators(decorators ...autorest.RespondDecorator) autorest.Respon } } -func NewAzureClient(subscriptionID, sigSubscriptionID, resourceGroupName, storageAccountName string, - cloud *azure.Environment, sharedGalleryTimeout time.Duration, pollingDuration time.Duration, - servicePrincipalToken, servicePrincipalTokenVault *adal.ServicePrincipalToken) (*AzureClient, error) { +type NewSDKAuthOptions struct { + AuthType string + ClientID string + ClientSecret string + ClientJWT string + ClientCertPath string + TenantID string + SubscriptionID string +} + +// Returns an Azure Client used for the Azure Resource Manager +// Also returns the Azure object ID for the authentication method used in the build +func NewAzureClient(ctx context.Context, isVHDBuild bool, cloud *environments.Environment, sharedGalleryTimeout time.Duration, pollingDuration time.Duration, newSdkAuthOptions NewSDKAuthOptions) (*AzureClient, *string, error) { var azureClient = &AzureClient{} maxlen := getInspectorMaxLength() + if cloud == nil || cloud.ResourceManager == nil { + // TODO Throw error message that helps users solve this problem + return nil, nil, fmt.Errorf("Azure Environment not configured correctly") + } + resourceManagerEndpoint, _ := cloud.ResourceManager.Endpoint() + resourceManagerAuthorizer, err := buildResourceManagerAuthorizer(ctx, newSdkAuthOptions, *cloud) + if err != nil { + return nil, nil, err + } - 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) + // Clients that have been ported to hashicorp/go-azure-sdk + azureClient.DisksClient = hashiDisksSDK.NewDisksClientWithBaseURI(*resourceManagerEndpoint) + azureClient.DisksClient.Client.Authorizer = authWrapper.AutorestAuthorizer(resourceManagerAuthorizer) + azureClient.DisksClient.Client.RequestInspector = withInspection(maxlen) + azureClient.DisksClient.Client.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient)) + azureClient.DisksClient.Client.UserAgent = fmt.Sprintf("%s %s", useragent.String(version.AzurePluginVersion.FormattedVersion()), azureClient.DisksClient.Client.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 = hashiVMSDK.NewVirtualMachinesClientWithBaseURI(*resourceManagerEndpoint) + azureClient.VirtualMachinesClient.Client.Authorizer = authWrapper.AutorestAuthorizer(resourceManagerAuthorizer) + azureClient.VirtualMachinesClient.Client.RequestInspector = withInspection(maxlen) + azureClient.VirtualMachinesClient.Client.ResponseInspector = byConcatDecorators(byInspecting(maxlen), templateCapture(azureClient), errorCapture(azureClient)) + azureClient.VirtualMachinesClient.Client.UserAgent = fmt.Sprintf("%s %s", useragent.String(version.AzurePluginVersion.FormattedVersion()), azureClient.VirtualMachinesClient.Client.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 = hashiSnapshotsSDK.NewSnapshotsClientWithBaseURI(*resourceManagerEndpoint) + azureClient.SnapshotsClient.Client.Authorizer = authWrapper.AutorestAuthorizer(resourceManagerAuthorizer) + azureClient.SnapshotsClient.Client.RequestInspector = withInspection(maxlen) + azureClient.SnapshotsClient.Client.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient)) + azureClient.SnapshotsClient.Client.UserAgent = fmt.Sprintf("%s %s", useragent.String(version.AzurePluginVersion.FormattedVersion()), azureClient.SnapshotsClient.Client.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 = compute.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.SecretsClient = hashiSecretsSDK.NewSecretsClientWithBaseURI(*resourceManagerEndpoint) + azureClient.SecretsClient.Client.Authorizer = authWrapper.AutorestAuthorizer(resourceManagerAuthorizer) + azureClient.SecretsClient.Client.RequestInspector = withInspection(maxlen) + azureClient.SecretsClient.Client.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient)) + azureClient.SecretsClient.Client.UserAgent = fmt.Sprintf("%s %s", useragent.String(version.AzurePluginVersion.FormattedVersion()), azureClient.SecretsClient.Client.UserAgent) + azureClient.SecretsClient.Client.PollingDuration = pollingDuration + + azureClient.VaultsClient = hashiVaultsSDK.NewVaultsClientWithBaseURI(*resourceManagerEndpoint) + azureClient.VaultsClient.Client.Authorizer = authWrapper.AutorestAuthorizer(resourceManagerAuthorizer) + azureClient.VaultsClient.Client.RequestInspector = withInspection(maxlen) + azureClient.VaultsClient.Client.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient)) + azureClient.VaultsClient.Client.UserAgent = fmt.Sprintf("%s %s", useragent.String(version.AzurePluginVersion.FormattedVersion()), azureClient.VaultsClient.Client.UserAgent) + azureClient.VaultsClient.Client.PollingDuration = pollingDuration + + azureClient.DeploymentsClient = hashiDeploymentsSDK.NewDeploymentsClientWithBaseURI(*resourceManagerEndpoint) + azureClient.DeploymentsClient.Client.Authorizer = authWrapper.AutorestAuthorizer(resourceManagerAuthorizer) + azureClient.DeploymentsClient.Client.RequestInspector = withInspection(maxlen) + azureClient.DeploymentsClient.Client.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient)) + azureClient.DeploymentsClient.Client.UserAgent = fmt.Sprintf("%s %s", useragent.String(version.AzurePluginVersion.FormattedVersion()), azureClient.DeploymentsClient.Client.UserAgent) + azureClient.DeploymentsClient.Client.PollingDuration = pollingDuration - azureClient.GalleryImagesClient = compute.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 - } + azureClient.DeploymentOperationsClient = hashiDeploymentOperationsSDK.NewDeploymentOperationsClientWithBaseURI(*resourceManagerEndpoint) + azureClient.DeploymentOperationsClient.Client.Authorizer = authWrapper.AutorestAuthorizer(resourceManagerAuthorizer) + azureClient.DeploymentOperationsClient.Client.RequestInspector = withInspection(maxlen) + azureClient.DeploymentOperationsClient.Client.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient)) + azureClient.DeploymentOperationsClient.Client.UserAgent = fmt.Sprintf("%s %s", useragent.String(version.AzurePluginVersion.FormattedVersion()), azureClient.DeploymentOperationsClient.Client.UserAgent) + azureClient.DeploymentOperationsClient.Client.PollingDuration = pollingDuration + + azureClient.ResourceGroupsClient = hashiGroupsSDK.NewResourceGroupsClientWithBaseURI(*resourceManagerEndpoint) + azureClient.ResourceGroupsClient.Client.Authorizer = authWrapper.AutorestAuthorizer(resourceManagerAuthorizer) + azureClient.ResourceGroupsClient.Client.RequestInspector = withInspection(maxlen) + azureClient.ResourceGroupsClient.Client.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient)) + azureClient.ResourceGroupsClient.Client.UserAgent = fmt.Sprintf("%s %s", useragent.String(version.AzurePluginVersion.FormattedVersion()), azureClient.ResourceGroupsClient.Client.UserAgent) + azureClient.ResourceGroupsClient.Client.PollingDuration = pollingDuration + + azureClient.ImagesClient = hashiImagesSDK.NewImagesClientWithBaseURI(*resourceManagerEndpoint) + azureClient.ImagesClient.Client.Authorizer = authWrapper.AutorestAuthorizer(resourceManagerAuthorizer) + azureClient.ImagesClient.Client.RequestInspector = withInspection(maxlen) + azureClient.ImagesClient.Client.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient)) + azureClient.ImagesClient.Client.UserAgent = fmt.Sprintf("%s %s", useragent.String(version.AzurePluginVersion.FormattedVersion()), azureClient.ImagesClient.Client.UserAgent) + azureClient.ImagesClient.Client.PollingDuration = pollingDuration - keyVaultURL, err := url.Parse(cloud.KeyVaultEndpoint) + // Clients that are using the existing SDK/auth logic + azureClient.StorageAccountsClient = hashiStorageAccountsSDK.NewStorageAccountsClientWithBaseURI(*resourceManagerEndpoint) + azureClient.StorageAccountsClient.Client.Authorizer = authWrapper.AutorestAuthorizer(resourceManagerAuthorizer) + azureClient.StorageAccountsClient.Client.RequestInspector = withInspection(maxlen) + azureClient.StorageAccountsClient.Client.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient)) + azureClient.StorageAccountsClient.Client.UserAgent = fmt.Sprintf("%s %s", useragent.String(version.AzurePluginVersion.FormattedVersion()), azureClient.StorageAccountsClient.Client.UserAgent) + azureClient.StorageAccountsClient.Client.PollingDuration = pollingDuration + + api := environments.AzurePublic().ResourceManager + networkMetaClient, err := hashiNetworkMetaSDK.NewClientWithBaseURI(api, func(c *resourcemanager.Client) { + c.Client.Authorizer = resourceManagerAuthorizer + c.Client.UserAgent = "some-user-agent" + }) if err != nil { - return nil, err + return nil, nil, err } + azureClient.NetworkMetaClient = *networkMetaClient - 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.NewVaultsClientWithBaseURI(cloud.ResourceManagerEndpoint, 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 - } + azureClient.GalleryImageVersionsClient = hashiGalleryImageVersionsSDK.NewGalleryImageVersionsClientWithBaseURI(*resourceManagerEndpoint) + azureClient.GalleryImageVersionsClient.Client.Authorizer = authWrapper.AutorestAuthorizer(resourceManagerAuthorizer) + azureClient.GalleryImageVersionsClient.Client.RequestInspector = withInspection(maxlen) + azureClient.GalleryImageVersionsClient.Client.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient)) + azureClient.GalleryImageVersionsClient.Client.UserAgent = fmt.Sprintf("%s %s", useragent.String(version.AzurePluginVersion.FormattedVersion()), azureClient.GalleryImageVersionsClient.Client.UserAgent) + azureClient.GalleryImageVersionsClient.Client.PollingDuration = sharedGalleryTimeout - storageClient, err := storage.NewClient( - storageAccountName, - *(*accountKeys.Keys)[0].Value, - cloud.StorageEndpointSuffix, - storage.DefaultAPIVersion, - true /*useHttps*/) + azureClient.GalleryImagesClient = hashiGalleryImagesSDK.NewGalleryImagesClientWithBaseURI(*resourceManagerEndpoint) + azureClient.GalleryImagesClient.Client.Authorizer = authWrapper.AutorestAuthorizer(resourceManagerAuthorizer) + azureClient.GalleryImagesClient.Client.RequestInspector = withInspection(maxlen) + azureClient.GalleryImagesClient.Client.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient)) + azureClient.GalleryImagesClient.Client.UserAgent = fmt.Sprintf("%s %s", useragent.String(version.AzurePluginVersion.FormattedVersion()), azureClient.GalleryImagesClient.Client.UserAgent) + azureClient.GalleryImagesClient.Client.PollingDuration = pollingDuration + // We only need the Blob Client to delete the OS VHD during VHD builds + if isVHDBuild { + storageAccountAuthorizer, err := buildStorageAuthorizer(ctx, newSdkAuthOptions, *cloud) if err != nil { - return nil, err + return nil, nil, err } - azureClient.BlobStorageClient = storageClient.GetBlobService() + blobClient := giovanniBlobStorageSDK.New() + azureClient.GiovanniBlobClient = blobClient + azureClient.GiovanniBlobClient.Authorizer = authWrapper.AutorestAuthorizer(storageAccountAuthorizer) + azureClient.GiovanniBlobClient.Client.RequestInspector = withInspection(maxlen) + azureClient.GiovanniBlobClient.Client.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient)) } - return azureClient, nil + token, err := resourceManagerAuthorizer.Token(ctx, &http.Request{}) + if err != nil { + return nil, nil, err + } + objectId, err := getObjectIdFromToken(token.AccessToken) + if err != nil { + return nil, nil, err + } + return azureClient, &objectId, nil } func getInspectorMaxLength() int64 { @@ -311,3 +301,93 @@ func getInspectorMaxLength() int64 { return i } + +const ( + AuthTypeDeviceLogin = "DeviceLogin" + AuthTypeMSI = "ManagedIdentity" + AuthTypeClientSecret = "ClientSecret" + AuthTypeClientCert = "ClientCertificate" + AuthTypeClientBearerJWT = "ClientBearerJWT" + AuthTypeAzureCLI = "AzureCLI" +) + +func buildResourceManagerAuthorizer(ctx context.Context, authOpts NewSDKAuthOptions, env environments.Environment) (auth.Authorizer, error) { + authorizer, err := buildAuthorizer(ctx, authOpts, env, env.ResourceManager) + if err != nil { + return nil, fmt.Errorf("building Resource Manager authorizer from credentials: %+v", err) + } + return authorizer, nil +} + +func buildStorageAuthorizer(ctx context.Context, authOpts NewSDKAuthOptions, env environments.Environment) (auth.Authorizer, error) { + authorizer, err := buildAuthorizer(ctx, authOpts, env, env.Storage) + if err != nil { + return nil, fmt.Errorf("building Storage authorizer from credentials: %+v", err) + } + return authorizer, nil +} + +func buildAuthorizer(ctx context.Context, authOpts NewSDKAuthOptions, env environments.Environment, api environments.Api) (auth.Authorizer, error) { + var authConfig auth.Credentials + switch authOpts.AuthType { + case AuthTypeDeviceLogin: + return nil, fmt.Errorf("DeviceLogin is not supported, however you can use the Azure CLI `az login --use-device-code` to use a device code, and then use CLI authentication") + case AuthTypeAzureCLI: + authConfig = auth.Credentials{ + Environment: env, + EnableAuthenticatingUsingAzureCLI: true, + } + case AuthTypeMSI: + authConfig = auth.Credentials{ + Environment: env, + EnableAuthenticatingUsingManagedIdentity: true, + } + case AuthTypeClientSecret: + authConfig = auth.Credentials{ + Environment: env, + EnableAuthenticatingUsingClientSecret: true, + ClientID: authOpts.ClientID, + ClientSecret: authOpts.ClientSecret, + TenantID: authOpts.TenantID, + } + case AuthTypeClientCert: + authConfig = auth.Credentials{ + Environment: env, + EnableAuthenticatingUsingClientCertificate: true, + ClientID: authOpts.ClientID, + ClientCertificatePath: authOpts.ClientCertPath, + ClientCertificatePassword: "", + } + case AuthTypeClientBearerJWT: + authConfig = auth.Credentials{ + Environment: env, + EnableAuthenticationUsingOIDC: true, + ClientID: authOpts.ClientID, + TenantID: authOpts.TenantID, + OIDCAssertionToken: authOpts.ClientJWT, + } + default: + panic("AuthType not set") + } + authorizer, err := auth.NewAuthorizerFromCredentials(ctx, authConfig, api) + if err != nil { + return nil, err + //fmt.Errorf("building Resource Manager authorizer from credentials: %+v", err) + } + return authorizer, nil +} + +func getObjectIdFromToken(token string) (string, error) { + claims := jwt.MapClaims{} + var p jwt.Parser + + var err error + + _, _, err = p.ParseUnverified(token, claims) + + if err != nil { + return "", err + } + return claims["oid"].(string), nil + +} diff --git a/builder/azure/arm/builder.go b/builder/azure/arm/builder.go index 0eb453e2..83584324 100644 --- a/builder/azure/arm/builder.go +++ b/builder/azure/arm/builder.go @@ -14,11 +14,11 @@ import ( "strings" "time" - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2021-11-01/compute" - 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/golang-jwt/jwt" + hashiImagesSDK "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-01/images" + hashiGalleryImagesSDK "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-03/galleryimages" + hashiStorageAccountsSDK "github.com/hashicorp/go-azure-sdk/resource-manager/storage/2022-09-01/storageaccounts" + + "github.com/hashicorp/go-azure-helpers/resourcemanager/commonids" "github.com/hashicorp/hcl/v2/hcldec" packerAzureCommon "github.com/hashicorp/packer-plugin-azure/builder/azure/common" "github.com/hashicorp/packer-plugin-azure/builder/azure/common/constants" @@ -40,8 +40,7 @@ type Builder struct { } const ( - DefaultSasBlobContainer = "system/Microsoft.Compute" - DefaultSecretName = "packerKeyVaultSecret" + DefaultSecretName = "packerKeyVaultSecret" ) func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() } @@ -66,7 +65,7 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) ui.Say("Running builder ...") - ctx, cancel := context.WithCancel(ctx) + ctx, cancel := context.WithTimeout(ctx, time.Minute*60) defer cancel() // FillParameters function captures authType and sets defaults. @@ -81,28 +80,34 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) b.stateBag.Put(constants.ArmManagedImageSubscription, b.config.ClientConfig.SubscriptionID) } + b.stateBag.Put(constants.ArmSubscription, 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 + // Pass in relevant auth information for hashicorp/go-azure-sdk + authOptions := NewSDKAuthOptions{ + AuthType: b.config.ClientConfig.AuthType(), + ClientID: b.config.ClientConfig.ClientID, + ClientSecret: b.config.ClientConfig.ClientSecret, + ClientJWT: b.config.ClientConfig.ClientJWT, + ClientCertPath: b.config.ClientConfig.ClientCertPath, + TenantID: b.config.ClientConfig.TenantID, + SubscriptionID: b.config.ClientConfig.SubscriptionID, } 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(), + azureClient, objectID, err := NewAzureClient( + ctx, + (b.config.ResourceGroupName != "" || b.config.StorageAccount != ""), + b.config.ClientConfig.NewCloudEnvironment(), b.config.SharedGalleryTimeout, b.config.PollingDurationTimeout, - spnCloud, - spnKeyVault) + authOptions, + ) if err != nil { return nil, err @@ -112,10 +117,11 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) if err := resolver.Resolve(&b.config); err != nil { return nil, err } + if b.config.ClientConfig.ObjectID == "" { - b.config.ClientConfig.ObjectID = getObjectIdFromToken(ui, spnCloud) + b.config.ClientConfig.ObjectID = *objectID } else { - ui.Message("You have provided Object_ID which is no longer needed, azure packer builder determines this dynamically from the authentication token") + ui.Message("You have provided Object_ID which is no longer needed, Azure Packer ARM builder determines this automatically using the Azure Access Token") } if b.config.ClientConfig.ObjectID == "" && b.config.OSType != constants.Target_Linux { @@ -123,20 +129,19 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) } if b.config.isManagedImage() { - _, err := azureClient.GroupsClient.Get(ctx, b.config.ManagedImageResourceGroupName) + groupId := commonids.NewResourceGroupID(b.config.ClientConfig.SubscriptionID, b.config.ManagedImageResourceGroupName) + _, err := azureClient.ResourceGroupsClient.Get(ctx, groupId) 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, "") + imageId := hashiImagesSDK.NewImageID(b.config.ClientConfig.SubscriptionID, b.config.ManagedImageResourceGroupName, b.config.ManagedImageName) + _, err = azureClient.ImagesClient.Get(ctx, imageId, hashiImagesSDK.DefaultGetOperationOptions()) 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) - } + err := azureClient.ImagesClient.DeleteThenPoll(ctx, imageId) if err != nil { return nil, fmt.Errorf("failed to delete the managed image named %s : %s", b.config.ManagedImageName, azureClient.LastError.Error()) } @@ -150,25 +155,26 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) } if b.config.BuildResourceGroupName != "" { - group, err := azureClient.GroupsClient.Get(ctx, b.config.BuildResourceGroupName) + buildGroupId := commonids.NewResourceGroupID(b.config.ClientConfig.SubscriptionID, b.config.BuildResourceGroupName) + group, err := azureClient.ResourceGroupsClient.Get(ctx, buildGroupId) 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.Location = group.Model.Location } b.config.validateLocationZoneResiliency(ui.Say) if b.config.StorageAccount != "" { - account, err := b.getBlobAccount(ctx, azureClient, b.config.ResourceGroupName, b.config.StorageAccount) + account, err := b.getBlobAccount(ctx, azureClient, b.config.ClientConfig.SubscriptionID, b.config.ResourceGroupName, b.config.StorageAccount) if err != nil { return nil, err } - b.config.storageAccountBlobEndpoint = *account.AccountProperties.PrimaryEndpoints.Blob + b.config.storageAccountBlobEndpoint = *account.Properties.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) + 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) } } @@ -190,7 +196,13 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) } // Validate that Shared Gallery Image exists before publishing to SIG if b.config.isPublishToSIG() { - _, err = azureClient.GalleryImagesClient.Get(ctx, b.config.SharedGalleryDestination.SigDestinationResourceGroup, b.config.SharedGalleryDestination.SigDestinationGalleryName, b.config.SharedGalleryDestination.SigDestinationImageName) + sigSubscriptionID := b.config.SharedGalleryDestination.SigDestinationSubscription + if sigSubscriptionID == "" { + sigSubscriptionID = b.stateBag.Get(constants.ArmSubscription).(string) + } + b.stateBag.Put(constants.ArmSharedImageGalleryDestinationSubscription, sigSubscriptionID) + galleryId := hashiGalleryImagesSDK.NewGalleryImageID(sigSubscriptionID, b.config.SharedGalleryDestination.SigDestinationResourceGroup, b.config.SharedGalleryDestination.SigDestinationGalleryName, b.config.SharedGalleryDestination.SigDestinationImageName) + _, err = azureClient.GalleryImagesClient.Get(ctx, galleryId) if err != nil { return nil, fmt.Errorf("the Shared Gallery Image '%s' to which to publish the managed image version to does not exist in the resource group '%s' or does not contain managed image '%s'", b.config.SharedGalleryDestination.SigDestinationGalleryName, b.config.SharedGalleryDestination.SigDestinationResourceGroup, b.config.SharedGalleryDestination.SigDestinationImageName) } @@ -215,12 +227,15 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) sourceImageSpecialized := false if b.config.SharedGallery.GalleryName != "" { client := azureClient.GalleryImagesClient - client.SubscriptionID = b.config.SharedGallery.Subscription - galleryImage, err := client.Get(ctx, b.config.SharedGallery.ResourceGroup, b.config.SharedGallery.GalleryName, b.config.SharedGallery.ImageName) + id := hashiGalleryImagesSDK.NewGalleryImageID(b.config.SharedGallery.Subscription, b.config.SharedGallery.ResourceGroup, b.config.SharedGallery.GalleryName, b.config.SharedGallery.ImageName) + galleryImage, err := client.Get(ctx, id) if err != nil { return nil, fmt.Errorf("the parent Shared Gallery Image '%s' from which to source the managed image version to does not exist in the resource group '%s' or does not contain managed image '%s'", b.config.SharedGallery.GalleryName, b.config.SharedGallery.ResourceGroup, b.config.SharedGallery.ImageName) } - if galleryImage.OsState == compute.OperatingSystemStateTypesSpecialized { + if galleryImage.Model == nil { + return nil, fmt.Errorf("SDK returned empty model for gallery image") + } + if galleryImage.Model.Properties.OsState == hashiGalleryImagesSDK.OperatingSystemStateTypesSpecialized { sourceImageSpecialized = true } } @@ -265,7 +280,7 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) NewStepDeployTemplate(azureClient, ui, &b.config, keyVaultDeploymentName, GetCommunicatorSpecificKeyVaultDeployment, KeyVaultTemplate), ) } else if b.config.Comm.Type == "winrm" { - steps = append(steps, NewStepCertificateInKeyVault(&azureClient.VaultClient, ui, &b.config, b.config.winrmCertificate)) + steps = append(steps, NewStepCertificateInKeyVault(azureClient, ui, &b.config, b.config.winrmCertificate)) } else { privateKey, err := ssh.ParseRawPrivateKey(b.config.Comm.SSHPrivateKey) if err != nil { @@ -280,7 +295,7 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) if err != nil { return nil, err } - steps = append(steps, NewStepCertificateInKeyVault(&azureClient.VaultClient, ui, &b.config, secret)) + steps = append(steps, NewStepCertificateInKeyVault(azureClient, ui, &b.config, secret)) } steps = append(steps, NewStepGetCertificate(azureClient, ui), @@ -368,15 +383,6 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) return nil, nil } - 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 - } - stateData := 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", @@ -396,8 +402,7 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) b.config.ManagedImageDataDiskSnapshotPrefix, stateData, b.stateBag.Get(constants.ArmKeepOSDisk).(bool), - template.(*CaptureTemplate), - getSasUrlFunc) + template.(*CaptureTemplate)) } return NewManagedImageArtifact(b.config.OSType, @@ -409,8 +414,7 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) b.config.ManagedImageDataDiskSnapshotPrefix, stateData, b.stateBag.Get(constants.ArmKeepOSDisk).(bool), - nil, - getSasUrlFunc) + nil) } if b.config.isPublishToSIG() { @@ -420,7 +424,6 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) if template, ok := b.stateBag.GetOk(constants.ArmCaptureTemplate); ok { return NewArtifact( template.(*CaptureTemplate), - getSasUrlFunc, b.config.OSType, stateData) } @@ -465,19 +468,21 @@ 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) +func (b *Builder) getBlobAccount(ctx context.Context, client *AzureClient, subscriptionId string, resourceGroupName string, storageAccountName string) (*hashiStorageAccountsSDK.StorageAccount, error) { + id := hashiStorageAccountsSDK.NewStorageAccountID(subscriptionId, resourceGroupName, storageAccountName) + account, err := client.StorageAccountsClient.GetProperties(ctx, id, hashiStorageAccountsSDK.DefaultGetPropertiesOperationOptions()) if err != nil { return nil, err } - return &account, err + return account.Model, 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.ArmNewSDKTags, b.config.AzureTags) stateBag.Put(constants.ArmComputeName, b.config.tmpComputeName) stateBag.Put(constants.ArmDeploymentName, b.config.tmpDeploymentName) @@ -539,34 +544,13 @@ func (b *Builder) setRuntimeParameters(stateBag multistep.StateBag) { } func (b *Builder) setTemplateParameters(stateBag multistep.StateBag) { - stateBag.Put(constants.ArmVirtualMachineCaptureParameters, b.config.toVirtualMachineCaptureParameters()) + stateBag.Put(constants.ArmNewVirtualMachineCaptureParameters, 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 index f175b30e..f7d557a3 100644 --- a/builder/azure/arm/builder_acc_test.go +++ b/builder/azure/arm/builder_acc_test.go @@ -37,7 +37,8 @@ import ( "testing" "time" - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2021-11-01/compute" + hashiGalleryImagesSDK "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-03/galleryimages" + "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-03/galleryimageversions" "github.com/hashicorp/packer-plugin-sdk/acctest" packersdk "github.com/hashicorp/packer-plugin-sdk/packer" "github.com/hashicorp/packer-plugin-sdk/retry" @@ -66,9 +67,12 @@ func TestBuilderAcc_SharedImageGallery_ARM64SpecializedLinuxSIG_WithChildImage(t return } + subscriptionID := os.Getenv("ARM_SUBSCRIPTION_ID") + createSharedImageGalleryDefinition(t, CreateSharedImageGalleryDefinitionParameters{ galleryImageName: "arm-linux-specialized-sig", imageSku: "22_04-lts-arm64", + subscriptionId: subscriptionID, imageOffer: "0001-com-ubuntu-server-jammy", imagePublisher: "canonical", isX64: false, @@ -77,12 +81,26 @@ func TestBuilderAcc_SharedImageGallery_ARM64SpecializedLinuxSIG_WithChildImage(t specialized: true, }) - defer deleteSharedImageGalleryDefinition(t, "arm-linux-specialized-sig", []string{"1.0.0", "1.0.1"}) + defer deleteSharedImageGalleryDefinition(t, subscriptionID, "arm-linux-specialized-sig", []string{"1.0.0", "1.0.1"}) // Create parent specialized shared gallery image acctest.TestPlugin(t, &acctest.PluginTestCase{ Name: "test-specialized-linux-sig", Type: "azure-arm", Template: string(armLinuxSpecialziedSIGTemplate), + Setup: func() error { + createSharedImageGalleryDefinition(t, CreateSharedImageGalleryDefinitionParameters{ + galleryImageName: "arm-linux-specialized-sig", + subscriptionId: os.Getenv("ARM_SUBSCRIPTION_ID"), + imageSku: "22_04-lts-arm64", + imageOffer: "0001-com-ubuntu-server-jammy", + imagePublisher: "canonical", + isX64: false, + isWindows: false, + useGenTwoVM: true, + specialized: true, + }) + return nil + }, Check: func(buildCommand *exec.Cmd, logfile string) error { if buildCommand.ProcessState != nil { if buildCommand.ProcessState.ExitCode() != 0 { @@ -91,9 +109,6 @@ func TestBuilderAcc_SharedImageGallery_ARM64SpecializedLinuxSIG_WithChildImage(t } return nil }, - Teardown: func() error { - return nil - }, }) // Create child image from a specialized parent @@ -124,15 +139,17 @@ func TestBuilderAcc_SharedImageGallery_WindowsSIG(t *testing.T) { return } + subscriptionID := os.Getenv("ARM_SUBSCRIPTION_ID") createSharedImageGalleryDefinition(t, CreateSharedImageGalleryDefinitionParameters{ galleryImageName: "windows-sig", imageSku: "2012-R2-Datacenter", imageOffer: "WindowsServer", imagePublisher: "MicrosoftWindowsServer", isX64: true, + subscriptionId: subscriptionID, isWindows: true, }) - defer deleteSharedImageGalleryDefinition(t, "windows-sig", []string{"1.0.0"}) + defer deleteSharedImageGalleryDefinition(t, subscriptionID, "windows-sig", []string{"1.0.0"}) acctest.TestPlugin(t, &acctest.PluginTestCase{ Name: "test-windows-sig", @@ -146,9 +163,6 @@ func TestBuilderAcc_SharedImageGallery_WindowsSIG(t *testing.T) { } return nil }, - Teardown: func() error { - return nil - }, }) } @@ -384,6 +398,7 @@ func TestBuilderAcc_rsaSHA2OnlyServer(t *testing.T) { type CreateSharedImageGalleryDefinitionParameters struct { galleryImageName string + subscriptionId string imageSku string imageOffer string imagePublisher string @@ -400,20 +415,21 @@ func createTestAzureClient(t *testing.T) AzureClient { // Use CLI auth for our test client b.config.ClientConfig.UseAzureCLIAuth = true _ = b.config.ClientConfig.FillParameters() - spnCloud, spnKeyVault, err := b.getServicePrincipalTokens(ui.Say) - if err != nil { - t.Fatalf("failed getting azure tokens: %s", err) + authOptions := NewSDKAuthOptions{ + AuthType: b.config.ClientConfig.AuthType(), + ClientID: b.config.ClientConfig.ClientID, + ClientSecret: b.config.ClientConfig.ClientSecret, + TenantID: b.config.ClientConfig.TenantID, + SubscriptionID: b.config.ClientConfig.SubscriptionID, } - azureClient, err := NewAzureClient( - b.config.ClientConfig.SubscriptionID, - b.config.SharedGalleryDestination.SigDestinationSubscription, - b.config.ResourceGroupName, - b.config.StorageAccount, - b.config.ClientConfig.CloudEnvironment(), + ui.Message("Creating test Azure Resource Manager (ARM) client ...") + azureClient, _, err := NewAzureClient( + context.TODO(), + true, + b.config.ClientConfig.NewCloudEnvironment(), b.config.SharedGalleryTimeout, b.config.PollingDurationTimeout, - spnCloud, - spnKeyVault) + authOptions) if err != nil { t.Fatalf("failed to create test azure client: %s", err) } @@ -422,58 +438,52 @@ func createTestAzureClient(t *testing.T) AzureClient { func createSharedImageGalleryDefinition(t *testing.T, params CreateSharedImageGalleryDefinitionParameters) { azureClient := createTestAzureClient(t) - osType := compute.OperatingSystemTypesLinux + osType := hashiGalleryImagesSDK.OperatingSystemTypesLinux if params.isWindows { - osType = compute.OperatingSystemTypesWindows + osType = hashiGalleryImagesSDK.OperatingSystemTypesWindows } - osState := compute.OperatingSystemStateTypesGeneralized + osState := hashiGalleryImagesSDK.OperatingSystemStateTypesGeneralized if params.specialized { - osState = compute.OperatingSystemStateTypesSpecialized + osState = hashiGalleryImagesSDK.OperatingSystemStateTypesSpecialized } - osArch := compute.ArchitectureArm64 + osArch := hashiGalleryImagesSDK.ArchitectureArmSixFour if params.isX64 { - osArch = compute.ArchitectureX64 + osArch = hashiGalleryImagesSDK.ArchitectureXSixFour } - hyperVGeneration := compute.HyperVGenerationV1 + hyperVGeneration := hashiGalleryImagesSDK.HyperVGenerationVOne if params.useGenTwoVM { - hyperVGeneration = compute.HyperVGenerationV2 + hyperVGeneration = hashiGalleryImagesSDK.HyperVGenerationVTwo } location := "southcentralus" - future, err := azureClient.GalleryImagesClient.CreateOrUpdate(context.TODO(), "packer-acceptance-test", "acctestgallery", params.galleryImageName, compute.GalleryImage{ - GalleryImageProperties: &compute.GalleryImageProperties{ + galleryId := hashiGalleryImagesSDK.NewGalleryImageID(params.subscriptionId, "packer-acceptance-test", "acctestgallery", params.galleryImageName) + _, err := azureClient.GalleryImagesClient.CreateOrUpdate(context.TODO(), galleryId, hashiGalleryImagesSDK.GalleryImage{ + Properties: &hashiGalleryImagesSDK.GalleryImageProperties{ OsType: osType, OsState: osState, - Architecture: osArch, - HyperVGeneration: hyperVGeneration, - Identifier: &compute.GalleryImageIdentifier{ - Publisher: ¶ms.imagePublisher, - Offer: ¶ms.imageOffer, - Sku: ¶ms.imageSku, + Architecture: &osArch, + HyperVGeneration: &hyperVGeneration, + Identifier: hashiGalleryImagesSDK.GalleryImageIdentifier{ + Publisher: params.imagePublisher, + Offer: params.imageOffer, + Sku: params.imageSku, }, }, - Location: &location, + Location: location, }) if err != nil { t.Fatalf("failed to create Gallery %s: %s", params.galleryImageName, err) } - err = future.WaitForCompletionRef(context.TODO(), azureClient.GalleryImagesClient.Client) - if err != nil { - t.Fatalf("failed to create Gallery %s: %s", params.galleryImageName, err) - } } -func deleteSharedImageGalleryDefinition(t *testing.T, galleryImageName string, imageVersions []string) { +func deleteSharedImageGalleryDefinition(t *testing.T, subscriptionID string, galleryImageName string, imageVersions []string) { azureClient := createTestAzureClient(t) for _, imageVersion := range imageVersions { // If we fail to delete a gallery version we should still try to delete other versions and the gallery // Its possible a build was canceled or failed mid test that would leave any of the builds incomplete // We still want to try and delete the Gallery to not leave behind orphaned resources to manually clean up - versionFuture, err := azureClient.GalleryImageVersionsClient.Delete(context.TODO(), "packer-acceptance-test", "acctestgallery", galleryImageName, imageVersion) - if err != nil { - t.Logf("failed to delete Gallery Image Version %s:%s %s", galleryImageName, imageVersion, err) - } - err = versionFuture.WaitForCompletionRef(context.TODO(), azureClient.GalleryImageVersionsClient.Client) + id := galleryimageversions.NewImageVersionID(subscriptionID, "packer-acceptance-test", "acctestgallery", galleryImageName, imageVersion) + err := azureClient.GalleryImageVersionsClient.DeleteThenPoll(context.TODO(), id) if err != nil { t.Logf("failed to delete Gallery Image Version %s:%s %s", galleryImageName, imageVersion, err) } @@ -483,12 +493,12 @@ func deleteSharedImageGalleryDefinition(t *testing.T, galleryImageName string, i RetryDelay: (&retry.Backoff{InitialBackoff: 2 * time.Second, MaxBackoff: 30 * time.Second, Multiplier: 2}).Linear, } err := retryConfig.Run(context.TODO(), func(ctx context.Context) error { - galleryFuture, err := azureClient.GalleryImagesClient.Delete(context.TODO(), "packer-acceptance-test", "acctestgallery", galleryImageName) + id := hashiGalleryImagesSDK.NewGalleryImageID(subscriptionID, "packer-acceptance-test", "acctestgallery", galleryImageName) + err := azureClient.GalleryImagesClient.DeleteThenPoll(context.TODO(), id) if err != nil { return err } - err = galleryFuture.WaitForCompletionRef(context.TODO(), azureClient.GalleryImagesClient.Client) - return err + return nil }) if err != nil { t.Fatalf("failed to delete Gallery %s: %s", galleryImageName, err) diff --git a/builder/azure/arm/builder_test.go b/builder/azure/arm/builder_test.go index 05e703be..9206b7bc 100644 --- a/builder/azure/arm/builder_test.go +++ b/builder/azure/arm/builder_test.go @@ -27,7 +27,7 @@ func TestStateBagShouldBePopulatedExpectedValues(t *testing.T) { constants.ArmNicName, constants.ArmResourceGroupName, constants.ArmStorageAccountName, - constants.ArmVirtualMachineCaptureParameters, + constants.ArmNewVirtualMachineCaptureParameters, constants.ArmPublicIPAddressName, constants.ArmAsyncResourceGroupDelete, } diff --git a/builder/azure/arm/config.go b/builder/azure/arm/config.go index 687d5d96..d19acbc6 100644 --- a/builder/azure/arm/config.go +++ b/builder/azure/arm/config.go @@ -23,8 +23,8 @@ import ( "github.com/hashicorp/packer-plugin-sdk/random" - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2021-11-01/compute" - "github.com/Azure/go-autorest/autorest/to" + hashiImagesSDK "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-01/images" + hashiVMSDK "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-01/virtualmachines" "github.com/masterzen/winrm" azcommon "github.com/hashicorp/packer-plugin-azure/builder/azure/common" @@ -112,7 +112,7 @@ type SharedImageGalleryDestination struct { type Spot struct { // Specify eviction policy for spot instance: "Deallocate" or "Delete". If this is set, a spot instance will be used. - EvictionPolicy compute.VirtualMachineEvictionPolicyTypes `mapstructure:"eviction_policy"` + EvictionPolicy hashiVMSDK.VirtualMachineEvictionPolicyTypes `mapstructure:"eviction_policy"` // How much should the VM cost maximally per hour. Specify -1 (or do not specify) to not evict based on price. MaxPrice float32 `mapstructure:"max_price"` } @@ -316,7 +316,7 @@ type Config struct { // type for a managed image. Valid values are Standard_LRS and Premium_LRS. // The default is Standard_LRS. ManagedImageStorageAccountType string `mapstructure:"managed_image_storage_account_type" required:"false"` - managedImageStorageAccountType compute.StorageAccountTypes + managedImageStorageAccountType hashiVMSDK.StorageAccountTypes // 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. @@ -524,7 +524,7 @@ type Config struct { // 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 + diskCachingType hashiVMSDK.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 @@ -639,26 +639,26 @@ func (c *Config) isPublishToSIG() bool { return c.SharedGalleryDestination.SigDestinationGalleryName != "" } -func (c *Config) toVirtualMachineCaptureParameters() *compute.VirtualMachineCaptureParameters { - return &compute.VirtualMachineCaptureParameters{ - DestinationContainerName: &c.CaptureContainerName, - VhdPrefix: &c.CaptureNamePrefix, - OverwriteVhds: to.BoolPtr(false), +func (c *Config) toVirtualMachineCaptureParameters() *hashiVMSDK.VirtualMachineCaptureParameters { + return &hashiVMSDK.VirtualMachineCaptureParameters{ + DestinationContainerName: c.CaptureContainerName, + VhdPrefix: c.CaptureNamePrefix, + OverwriteVhds: false, } } -func (c *Config) toImageParameters() *compute.Image { - return &compute.Image{ - ImageProperties: &compute.ImageProperties{ - SourceVirtualMachine: &compute.SubResource{ - ID: to.StringPtr(c.toVMID()), +func (c *Config) toImageParameters() *hashiImagesSDK.Image { + return &hashiImagesSDK.Image{ + Properties: &hashiImagesSDK.ImageProperties{ + SourceVirtualMachine: &hashiImagesSDK.SubResource{ + Id: azcommon.StringPtr(c.toVMID()), }, - StorageProfile: &compute.ImageStorageProfile{ - ZoneResilient: to.BoolPtr(c.ManagedImageZoneResilient), + StorageProfile: &hashiImagesSDK.ImageStorageProfile{ + ZoneResilient: azcommon.BoolPtr(c.ManagedImageZoneResilient), }, }, - Location: to.StringPtr(c.Location), - Tags: azcommon.MapToAzureTags(c.AzureTags), + Location: *azcommon.StringPtr(c.Location), + Tags: &c.AzureTags, } } @@ -984,11 +984,11 @@ func provideDefaultValues(c *Config) { } if c.ManagedImageStorageAccountType == "" { - c.managedImageStorageAccountType = compute.StorageAccountTypesStandardLRS + c.managedImageStorageAccountType = hashiVMSDK.StorageAccountTypesStandardLRS } if c.DiskCachingType == "" { - c.diskCachingType = compute.CachingTypesReadWrite + c.diskCachingType = hashiVMSDK.CachingTypesReadWrite } if c.ImagePublisher != "" && c.ImageVersion == "" { @@ -1331,22 +1331,22 @@ func assertRequiredParametersSet(c *Config, errs *packersdk.MultiError) { ///////////////////////////////////////////// // Storage if c.Spot.EvictionPolicy != "" { - if c.Spot.EvictionPolicy != compute.VirtualMachineEvictionPolicyTypesDelete && c.Spot.EvictionPolicy != compute.VirtualMachineEvictionPolicyTypesDeallocate { - errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("The spot.eviction_policy %q is invalid, eviction_policy must be %q, %q, or unset", c.Spot.EvictionPolicy, compute.VirtualMachineEvictionPolicyTypesDelete, compute.VirtualMachineEvictionPolicyTypesDeallocate)) + if c.Spot.EvictionPolicy != hashiVMSDK.VirtualMachineEvictionPolicyTypesDelete && c.Spot.EvictionPolicy != hashiVMSDK.VirtualMachineEvictionPolicyTypesDeallocate { + errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("The spot.eviction_policy %q is invalid, eviction_policy must be %q, %q, or unset", c.Spot.EvictionPolicy, hashiVMSDK.VirtualMachineEvictionPolicyTypesDelete, hashiVMSDK.VirtualMachineEvictionPolicyTypesDeallocate)) } } else { if c.Spot.MaxPrice != 0 { - errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("Setting a spot.max_price without an spot.eviction_policy is invalid, eviction_policy must be %q or %q if max_price is set", compute.VirtualMachineEvictionPolicyTypesDelete, compute.VirtualMachineEvictionPolicyTypesDeallocate)) + errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("Setting a spot.max_price without an spot.eviction_policy is invalid, eviction_policy must be %q or %q if max_price is set", hashiVMSDK.VirtualMachineEvictionPolicyTypesDelete, hashiVMSDK.VirtualMachineEvictionPolicyTypesDeallocate)) } } ///////////////////////////////////////////// // Storage switch c.ManagedImageStorageAccountType { - case "", string(compute.StorageAccountTypesStandardLRS): - c.managedImageStorageAccountType = compute.StorageAccountTypesStandardLRS - case string(compute.StorageAccountTypesPremiumLRS): - c.managedImageStorageAccountType = compute.StorageAccountTypesPremiumLRS + case "", string(hashiVMSDK.StorageAccountTypesStandardLRS): + c.managedImageStorageAccountType = hashiVMSDK.StorageAccountTypesStandardLRS + case string(hashiVMSDK.StorageAccountTypesPremiumLRS): + c.managedImageStorageAccountType = hashiVMSDK.StorageAccountTypesPremiumLRS default: errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("The managed_image_storage_account_type %q is invalid", c.ManagedImageStorageAccountType)) } @@ -1356,12 +1356,12 @@ func assertRequiredParametersSet(c *Config, errs *packersdk.MultiError) { } 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 + case string(hashiVMSDK.CachingTypesNone): + c.diskCachingType = hashiVMSDK.CachingTypesNone + case string(hashiVMSDK.CachingTypesReadOnly): + c.diskCachingType = hashiVMSDK.CachingTypesReadOnly + case "", string(hashiVMSDK.CachingTypesReadWrite): + c.diskCachingType = hashiVMSDK.CachingTypesReadWrite default: errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("The disk_caching_type %q is invalid", c.DiskCachingType)) } diff --git a/builder/azure/arm/config.hcl2spec.go b/builder/azure/arm/config.hcl2spec.go index e8b2f68c..b6bf1fd2 100644 --- a/builder/azure/arm/config.hcl2spec.go +++ b/builder/azure/arm/config.hcl2spec.go @@ -3,7 +3,7 @@ package arm import ( - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2021-11-01/compute" + "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-01/virtualmachines" "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer-plugin-sdk/template/config" "github.com/zclconf/go-cty/cty" @@ -187,6 +187,109 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "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}, +<<<<<<< HEAD + "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}, + "spot": &hcldec.BlockSpec{TypeName: "spot", Nested: hcldec.ObjectSpec((*FlatSpot)(nil).HCL2Spec())}, + "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}, + "disk_encryption_set_id": &hcldec.AttrSpec{Name: "disk_encryption_set_id", 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}, + "custom_data": &hcldec.AttrSpec{Name: "custom_data", Type: cty.String, Required: false}, + "user_data_file": &hcldec.AttrSpec{Name: "user_data_file", Type: cty.String, Required: false}, + "user_data": &hcldec.AttrSpec{Name: "user_data", Type: cty.String, Required: false}, + "custom_script": &hcldec.AttrSpec{Name: "custom_script", 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}, + "license_type": &hcldec.AttrSpec{Name: "license_type", Type: cty.String, Required: false}, + "secure_boot_enabled": &hcldec.AttrSpec{Name: "secure_boot_enabled", Type: cty.Bool, Required: false}, + "encryption_at_host": &hcldec.AttrSpec{Name: "encryption_at_host", Type: cty.Bool, Required: false}, + "vtpm_enabled": &hcldec.AttrSpec{Name: "vtpm_enabled", Type: cty.Bool, 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}, +======= "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}, @@ -236,7 +339,6 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "custom_resource_build_prefix": &hcldec.AttrSpec{Name: "custom_resource_build_prefix", Type: cty.String, Required: false}, "license_type": &hcldec.AttrSpec{Name: "license_type", Type: cty.String, Required: false}, "secure_boot_enabled": &hcldec.AttrSpec{Name: "secure_boot_enabled", Type: cty.Bool, Required: false}, - "encryption_at_host": &hcldec.AttrSpec{Name: "encryption_at_host", Type: cty.Bool, Required: false}, "vtpm_enabled": &hcldec.AttrSpec{Name: "vtpm_enabled", Type: cty.Bool, 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}, @@ -288,6 +390,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "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}, +>>>>>>> 04a4a81 (Switch AuthType back to private variable and use helper function to get it, switch from MeClient to ServicePrincipals client to get objectID) } return s } @@ -396,8 +499,8 @@ func (*FlatSharedImageGalleryDestination) HCL2Spec() map[string]hcldec.Spec { // FlatSpot is an auto-generated flat version of Spot. // Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. type FlatSpot struct { - EvictionPolicy *compute.VirtualMachineEvictionPolicyTypes `mapstructure:"eviction_policy" cty:"eviction_policy" hcl:"eviction_policy"` - MaxPrice *float32 `mapstructure:"max_price" cty:"max_price" hcl:"max_price"` + EvictionPolicy *virtualmachines.VirtualMachineEvictionPolicyTypes `mapstructure:"eviction_policy" cty:"eviction_policy" hcl:"eviction_policy"` + MaxPrice *float32 `mapstructure:"max_price" cty:"max_price" hcl:"max_price"` } // FlatMapstructure returns a new FlatSpot. diff --git a/builder/azure/arm/config_test.go b/builder/azure/arm/config_test.go index d01791c5..3ca912d6 100644 --- a/builder/azure/arm/config_test.go +++ b/builder/azure/arm/config_test.go @@ -11,8 +11,8 @@ import ( "testing" "time" - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2021-11-01/compute" "github.com/google/go-cmp/cmp" + hashiVMSDK "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-01/virtualmachines" "github.com/hashicorp/packer-plugin-azure/builder/azure/common/constants" sdkconfig "github.com/hashicorp/packer-plugin-sdk/template/config" ) @@ -128,11 +128,11 @@ func TestConfigShouldBeAbleToOverrideDefaultedValues(t *testing.T) { t.Errorf("Expected 'vm_size' to be set to 'override_vm_size', but found %q!", c.VMSize) } - if c.managedImageStorageAccountType != compute.StorageAccountTypesPremiumLRS { + if c.managedImageStorageAccountType != hashiVMSDK.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 { + if c.diskCachingType != hashiVMSDK.CachingTypesNone { t.Errorf("Expected 'disk_caching_type' to be set to 'None', but found %q!", c.diskCachingType) } @@ -633,15 +633,15 @@ func TestConfigShouldTransformToVirtualMachineCaptureParameters(t *testing.T) { } 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.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.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 { + if parameters.OverwriteVhds != false { t.Error("Expected OverwriteVhds to be false, but it was not.") } } @@ -1097,7 +1097,7 @@ func TestConfigZoneResilientShouldDefaultToFalse(t *testing.T) { } p := c.toImageParameters() - if *p.ImageProperties.StorageProfile.ZoneResilient { + if *p.Properties.StorageProfile.ZoneResilient { t.Fatal("expected zone resilient default to be false") } } @@ -1121,7 +1121,7 @@ func TestConfigZoneResilientSetFromConfig(t *testing.T) { } p := c.toImageParameters() - if *p.ImageProperties.StorageProfile.ZoneResilient == false { + if *p.Properties.StorageProfile.ZoneResilient == false { t.Fatal("expected managed image zone resilient to be true from config") } } diff --git a/builder/azure/arm/resource_resolver.go b/builder/azure/arm/resource_resolver.go index e9cf204e..cd7f2f54 100644 --- a/builder/azure/arm/resource_resolver.go +++ b/builder/azure/arm/resource_resolver.go @@ -1,6 +1,5 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 - package arm // Code to resolve resources that are required by the API. These resources @@ -16,13 +15,15 @@ import ( "fmt" "strings" - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2021-11-01/compute" + "github.com/hashicorp/go-azure-helpers/resourcemanager/commonids" + hashiImagesSDK "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-01/images" + hashiSubnetsSDK "github.com/hashicorp/go-azure-sdk/resource-manager/network/2022-09-01/subnets" ) type resourceResolver struct { client *AzureClient - findVirtualNetworkResourceGroup func(*AzureClient, string) (string, error) - findVirtualNetworkSubnet func(*AzureClient, string, string) (string, error) + findVirtualNetworkResourceGroup func(*AzureClient, string, string) (string, error) + findVirtualNetworkSubnet func(*AzureClient, string, string, string) (string, error) } func newResourceResolver(client *AzureClient) *resourceResolver { @@ -35,12 +36,12 @@ func newResourceResolver(client *AzureClient) *resourceResolver { func (s *resourceResolver) Resolve(c *Config) error { if s.shouldResolveResourceGroup(c) { - resourceGroupName, err := s.findVirtualNetworkResourceGroup(s.client, c.VirtualNetworkName) + resourceGroupName, err := s.findVirtualNetworkResourceGroup(s.client, c.ClientConfig.SubscriptionID, c.VirtualNetworkName) if err != nil { return err } - subnetName, err := s.findVirtualNetworkSubnet(s.client, resourceGroupName, c.VirtualNetworkName) + subnetName, err := s.findVirtualNetworkSubnet(s.client, c.ClientConfig.SubscriptionID, resourceGroupName, c.VirtualNetworkName) if err != nil { return err } @@ -50,12 +51,12 @@ func (s *resourceResolver) Resolve(c *Config) error { } if s.shouldResolveManagedImageName(c) { - image, err := findManagedImageByName(s.client, c.CustomManagedImageName, c.CustomManagedImageResourceGroupName) + image, err := findManagedImageByName(s.client, c.CustomManagedImageName, c.ClientConfig.SubscriptionID, c.CustomManagedImageResourceGroupName) if err != nil { return err } - c.customManagedImageID = *image.ID + c.customManagedImageID = *image.Id } return nil @@ -75,41 +76,34 @@ func getResourceGroupNameFromId(id string) string { return xs[4] } -func findManagedImageByName(client *AzureClient, name, resourceGroupName string) (*compute.Image, error) { - images, err := client.ImagesClient.ListByResourceGroupComplete(context.TODO(), resourceGroupName) +func findManagedImageByName(client *AzureClient, name, subscriptionId, resourceGroupName string) (*hashiImagesSDK.Image, error) { + id := commonids.NewResourceGroupID(subscriptionId, resourceGroupName) + images, err := client.ImagesClient.ListByResourceGroupComplete(context.TODO(), id) if err != nil { return nil, err } - for images.NotDone() { - image := images.Value() + for _, image := range images.Items { 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()) +func findVirtualNetworkResourceGroup(client *AzureClient, subscriptionId, name string) (string, error) { + virtualNetworks, err := client.NetworkMetaClient.VirtualNetworks.ListAllComplete(context.TODO(), commonids.NewSubscriptionID(subscriptionId)) if err != nil { return "", err } resourceGroupNames := make([]string, 0) - for virtualNetworks.NotDone() { - virtualNetwork := virtualNetworks.Value() + for _, virtualNetwork := range virtualNetworks.Items { if strings.EqualFold(name, *virtualNetwork.Name) { - rgn := getResourceGroupNameFromId(*virtualNetwork.ID) + rgn := getResourceGroupNameFromId(*virtualNetwork.Id) resourceGroupNames = append(resourceGroupNames, rgn) } - if err = virtualNetworks.Next(); err != nil { - return "", err - } } if len(resourceGroupNames) == 0 { @@ -123,13 +117,14 @@ func findVirtualNetworkResourceGroup(client *AzureClient, name string) (string, return resourceGroupNames[0], nil } -func findVirtualNetworkSubnet(client *AzureClient, resourceGroupName string, name string) (string, error) { - subnets, err := client.SubnetsClient.List(context.TODO(), resourceGroupName, name) +func findVirtualNetworkSubnet(client *AzureClient, subscriptionId string, resourceGroupName string, name string) (string, error) { + + subnets, err := client.NetworkMetaClient.Subnets.List(context.TODO(), hashiSubnetsSDK.NewVirtualNetworkID(subscriptionId, resourceGroupName, name)) if err != nil { return "", err } - subnetList := subnets.Values() // only first page of subnets, but only interested in ==0 or >1 + subnetList := *subnets.Model 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) diff --git a/builder/azure/arm/resource_resolver_test.go b/builder/azure/arm/resource_resolver_test.go index 475e0d73..581a6f16 100644 --- a/builder/azure/arm/resource_resolver_test.go +++ b/builder/azure/arm/resource_resolver_test.go @@ -81,10 +81,10 @@ func TestResourceResolverSetVirtualNetworkResourceGroupName(t *testing.T) { func newTestResourceResolver() resourceResolver { return resourceResolver{ client: nil, - findVirtualNetworkResourceGroup: func(*AzureClient, string) (string, error) { + findVirtualNetworkResourceGroup: func(*AzureClient, string, string) (string, error) { return "findVirtualNetworkResourceGroup is mocked", nil }, - findVirtualNetworkSubnet: func(*AzureClient, string, string) (string, error) { + findVirtualNetworkSubnet: func(*AzureClient, string, string, string) (string, error) { return "findVirtualNetworkSubnet is mocked", nil }, } diff --git a/builder/azure/arm/step_capture_image.go b/builder/azure/arm/step_capture_image.go index 25fbc00a..9681cba8 100644 --- a/builder/azure/arm/step_capture_image.go +++ b/builder/azure/arm/step_capture_image.go @@ -7,7 +7,8 @@ import ( "context" "fmt" - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2021-11-01/compute" + hashiImagesSDK "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-01/images" + hashiVMSDK "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-01/virtualmachines" "github.com/hashicorp/packer-plugin-azure/builder/azure/common/constants" "github.com/hashicorp/packer-plugin-sdk/multistep" packersdk "github.com/hashicorp/packer-plugin-sdk/packer" @@ -15,9 +16,9 @@ import ( 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 + generalizeVM func(ctx context.Context, vmId hashiVMSDK.VirtualMachineId) error + captureVhd func(ctx context.Context, vmId hashiVMSDK.VirtualMachineId, parameters *hashiVMSDK.VirtualMachineCaptureParameters) error + captureManagedImage func(ctx context.Context, subscriptionId string, resourceGroupName string, imageName string, parameters *hashiImagesSDK.Image) error get func(client *AzureClient) *CaptureTemplate say func(message string) error func(e error) @@ -44,28 +45,29 @@ func NewStepCaptureImage(client *AzureClient, ui packersdk.Ui) *StepCaptureImage return step } -func (s *StepCaptureImage) generalize(resourceGroupName string, computeName string) error { - _, err := s.client.Generalize(context.TODO(), resourceGroupName, computeName) +func (s *StepCaptureImage) generalize(ctx context.Context, vmId hashiVMSDK.VirtualMachineId) error { + _, err := s.client.Generalize(ctx, vmId) 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) +func (s *StepCaptureImage) captureImageFromVM(ctx context.Context, subscriptionId string, resourceGroupName string, imageName string, image *hashiImagesSDK.Image) error { + id := hashiImagesSDK.NewImageID(subscriptionId, resourceGroupName, imageName) + err := s.client.ImagesClient.CreateOrUpdateThenPoll(ctx, id, *image) if err != nil { s.say(s.client.LastError.Error()) } - return f.WaitForCompletionRef(ctx, s.client.ImagesClient.Client) + return err } -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 { +func (s *StepCaptureImage) captureImage(ctx context.Context, vmId hashiVMSDK.VirtualMachineId, parameters *hashiVMSDK.VirtualMachineCaptureParameters) error { + if err := s.client.VirtualMachinesClient.CaptureThenPoll(ctx, vmId, *parameters); err != nil { s.say(s.client.LastError.Error()) + return err } - return f.WaitForCompletionRef(ctx, s.client.VirtualMachinesClient.Client) + return nil } func (s *StepCaptureImage) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { @@ -73,60 +75,60 @@ func (s *StepCaptureImage) Run(ctx context.Context, state multistep.StateBag) mu 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 vmCaptureParameters = state.Get(constants.ArmNewVirtualMachineCaptureParameters).(*hashiVMSDK.VirtualMachineCaptureParameters) + var imageParameters = state.Get(constants.ArmImageParameters).(*hashiImagesSDK.Image) + var subscriptionId = state.Get(constants.ArmSubscription).(string) var isManagedImage = state.Get(constants.ArmIsManagedImage).(bool) var isSIGImage = state.Get(constants.ArmIsSIGImage).(bool) var skipGeneralization = state.Get(constants.ArmSharedImageGalleryDestinationSpecialized).(bool) + + vmId := hashiVMSDK.NewVirtualMachineID(subscriptionId, resourceGroupName, computeName) s.say(fmt.Sprintf(" -> Compute ResourceGroupName : '%s'", resourceGroupName)) s.say(fmt.Sprintf(" -> Compute Name : '%s'", computeName)) s.say(fmt.Sprintf(" -> Compute Location : '%s'", location)) - var err error if skipGeneralization { s.say("Skipping generalization of Compute Gallery Image") } else { s.say("Generalizing machine ...") - err = s.generalizeVM(resourceGroupName, computeName) - } + err := s.generalizeVM(ctx, vmId) + + if err == nil { + if isManagedImage { + s.say("Capturing image ...") + 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(" -> Image ResourceGroupName : '%s'", targetManagedImageResourceGroupName)) + s.say(fmt.Sprintf(" -> Image Name : '%s'", targetManagedImageName)) + s.say(fmt.Sprintf(" -> Image Location : '%s'", targetManagedImageLocation)) + err = s.captureManagedImage(ctx, subscriptionId, targetManagedImageResourceGroupName, targetManagedImageName, imageParameters) + } else if isSIGImage { + // It's possible to create SIG image + return multistep.ActionContinue + } else { + s.say("Capturing VHD ...") + err = s.captureVhd(ctx, vmId, vmCaptureParameters) + } + } + if err != nil { + state.Put(constants.Error, err) + s.error(err) - if err == nil { - if isManagedImage { - s.say("Capturing image ...") - 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(" -> 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 if isSIGImage { - // It's possible to create SIG image - return multistep.ActionContinue - } else { - s.say("Capturing VHD ...") - err = s.captureVhd(ctx, resourceGroupName, computeName, vmCaptureParameters) + return multistep.ActionHalt } - } - 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) } - - // 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 } diff --git a/builder/azure/arm/step_capture_image_test.go b/builder/azure/arm/step_capture_image_test.go index e431f926..092a5647 100644 --- a/builder/azure/arm/step_capture_image_test.go +++ b/builder/azure/arm/step_capture_image_test.go @@ -8,17 +8,18 @@ import ( "fmt" "testing" - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2021-11-01/compute" + hashiImagesSDK "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-01/images" + hashiVMSDK "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-01/virtualmachines" "github.com/hashicorp/packer-plugin-azure/builder/azure/common/constants" "github.com/hashicorp/packer-plugin-sdk/multistep" ) func TestStepCaptureImageShouldFailIfCaptureFails(t *testing.T) { var testSubject = &StepCaptureImage{ - captureVhd: func(context.Context, string, string, *compute.VirtualMachineCaptureParameters) error { + captureVhd: func(context.Context, hashiVMSDK.VirtualMachineId, *hashiVMSDK.VirtualMachineCaptureParameters) error { return fmt.Errorf("!! Unit Test FAIL !!") }, - generalizeVM: func(string, string) error { + generalizeVM: func(context.Context, hashiVMSDK.VirtualMachineId) error { return nil }, get: func(client *AzureClient) *CaptureTemplate { @@ -42,8 +43,10 @@ func TestStepCaptureImageShouldFailIfCaptureFails(t *testing.T) { func TestStepCaptureImageShouldPassIfCapturePasses(t *testing.T) { var testSubject = &StepCaptureImage{ - captureVhd: func(context.Context, string, string, *compute.VirtualMachineCaptureParameters) error { return nil }, - generalizeVM: func(string, string) error { + captureVhd: func(ctx context.Context, vmId hashiVMSDK.VirtualMachineId, parameters *hashiVMSDK.VirtualMachineCaptureParameters) error { + return nil + }, + generalizeVM: func(context.Context, hashiVMSDK.VirtualMachineId) error { return nil }, get: func(client *AzureClient) *CaptureTemplate { @@ -68,8 +71,10 @@ func TestStepCaptureImageShouldPassIfCapturePasses(t *testing.T) { func TestStepCaptureImageShouldCallGeneralizeIfSpecializedIsFalse(t *testing.T) { generalizeCount := 0 var testSubject = &StepCaptureImage{ - captureVhd: func(context.Context, string, string, *compute.VirtualMachineCaptureParameters) error { return nil }, - generalizeVM: func(string, string) error { + captureVhd: func(context.Context, hashiVMSDK.VirtualMachineId, *hashiVMSDK.VirtualMachineCaptureParameters) error { + return nil + }, + generalizeVM: func(context.Context, hashiVMSDK.VirtualMachineId) error { generalizeCount++ return nil }, @@ -98,8 +103,10 @@ func TestStepCaptureImageShouldCallGeneralizeIfSpecializedIsFalse(t *testing.T) func TestStepCaptureImageShouldNotCallGeneralizeIfSpecializedIsTrue(t *testing.T) { generalizeCount := 0 var testSubject = &StepCaptureImage{ - captureVhd: func(context.Context, string, string, *compute.VirtualMachineCaptureParameters) error { return nil }, - generalizeVM: func(string, string) error { + captureVhd: func(context.Context, hashiVMSDK.VirtualMachineId, *hashiVMSDK.VirtualMachineCaptureParameters) error { + return nil + }, + generalizeVM: func(context.Context, hashiVMSDK.VirtualMachineId) error { generalizeCount++ return nil }, @@ -131,20 +138,20 @@ func TestStepCaptureImageShouldTakeStepArgumentsFromStateBag(t *testing.T) { var actualResourceGroupName string var actualComputeName string - var actualVirtualMachineCaptureParameters *compute.VirtualMachineCaptureParameters + var actualVirtualMachineCaptureParameters *hashiVMSDK.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 + captureVhd: func(ctx context.Context, id hashiVMSDK.VirtualMachineId, parameters *hashiVMSDK.VirtualMachineCaptureParameters) error { + actualResourceGroupName = id.ResourceGroupName + actualComputeName = id.VirtualMachineName actualVirtualMachineCaptureParameters = parameters return nil }, - generalizeVM: func(string, string) error { + generalizeVM: func(context.Context, hashiVMSDK.VirtualMachineId) error { return nil }, get: func(client *AzureClient) *CaptureTemplate { @@ -163,7 +170,7 @@ func TestStepCaptureImageShouldTakeStepArgumentsFromStateBag(t *testing.T) { var expectedComputeName = stateBag.Get(constants.ArmComputeName).(string) var expectedResourceGroupName = stateBag.Get(constants.ArmResourceGroupName).(string) - var expectedVirtualMachineCaptureParameters = stateBag.Get(constants.ArmVirtualMachineCaptureParameters).(*compute.VirtualMachineCaptureParameters) + var expectedVirtualMachineCaptureParameters = stateBag.Get(constants.ArmNewVirtualMachineCaptureParameters).(*hashiVMSDK.VirtualMachineCaptureParameters) var expectedCaptureTemplate = stateBag.Get(constants.ArmCaptureTemplate).(*CaptureTemplate) if actualComputeName != expectedComputeName { @@ -189,12 +196,13 @@ func createTestStateBagStepCaptureImage() multistep.StateBag { 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.ArmSubscription, "Unit Test: SubscriptionId") + stateBag.Put(constants.ArmNewVirtualMachineCaptureParameters, &hashiVMSDK.VirtualMachineCaptureParameters{}) stateBag.Put(constants.ArmIsManagedImage, false) stateBag.Put(constants.ArmManagedImageResourceGroupName, "") stateBag.Put(constants.ArmManagedImageName, "") - stateBag.Put(constants.ArmImageParameters, &compute.Image{}) + stateBag.Put(constants.ArmImageParameters, &hashiImagesSDK.Image{}) stateBag.Put(constants.ArmIsSIGImage, false) stateBag.Put(constants.ArmSharedImageGalleryDestinationSpecialized, false) diff --git a/builder/azure/arm/step_certificate_in_keyvault.go b/builder/azure/arm/step_certificate_in_keyvault.go index 1a9a8096..ffa7a0a5 100644 --- a/builder/azure/arm/step_certificate_in_keyvault.go +++ b/builder/azure/arm/step_certificate_in_keyvault.go @@ -7,7 +7,7 @@ import ( "context" "fmt" - "github.com/hashicorp/packer-plugin-azure/builder/azure/common" + hashiSecretsSDK "github.com/hashicorp/go-azure-sdk/resource-manager/keyvault/2023-02-01/secrets" "github.com/hashicorp/packer-plugin-azure/builder/azure/common/constants" "github.com/hashicorp/packer-plugin-sdk/multistep" packersdk "github.com/hashicorp/packer-plugin-sdk/packer" @@ -15,29 +15,42 @@ import ( type StepCertificateInKeyVault struct { config *Config - client common.AZVaultClientIface + client *AzureClient + set func(ctx context.Context, id hashiSecretsSDK.SecretId) error say func(message string) error func(e error) certificate string } -func NewStepCertificateInKeyVault(cli common.AZVaultClientIface, ui packersdk.Ui, config *Config, certificate string) *StepCertificateInKeyVault { +func NewStepCertificateInKeyVault(client *AzureClient, ui packersdk.Ui, config *Config, certificate string) *StepCertificateInKeyVault { var step = &StepCertificateInKeyVault{ - client: cli, + client: client, config: config, say: func(message string) { ui.Say(message) }, error: func(e error) { ui.Error(e.Error()) }, certificate: certificate, } + step.set = step.setCertificate return step } +func (s *StepCertificateInKeyVault) setCertificate(ctx context.Context, id hashiSecretsSDK.SecretId) error { + _, err := s.client.SecretsClient.CreateOrUpdate(ctx, id, hashiSecretsSDK.SecretCreateOrUpdateParameters{ + Properties: hashiSecretsSDK.SecretProperties{ + Value: &s.certificate, + }, + }) + + return err +} 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.certificate) + var subscriptionId = state.Get(constants.ArmSubscription).(string) + var resourceGroupName = state.Get(constants.ArmResourceGroupName).(string) + id := hashiSecretsSDK.NewSecretID(subscriptionId, resourceGroupName, keyVaultName, DefaultSecretName) + err := s.set(ctx, id) if err != nil { s.error(fmt.Errorf("Error setting winrm cert in custom keyvault: %s", err)) return multistep.ActionHalt diff --git a/builder/azure/arm/step_certificate_in_keyvault_test.go b/builder/azure/arm/step_certificate_in_keyvault_test.go index d668d7a1..618c4188 100644 --- a/builder/azure/arm/step_certificate_in_keyvault_test.go +++ b/builder/azure/arm/step_certificate_in_keyvault_test.go @@ -4,44 +4,40 @@ package arm import ( - "bytes" "context" + "fmt" "testing" + hashiSecretsSDK "github.com/hashicorp/go-azure-sdk/resource-manager/keyvault/2023-02-01/secrets" azcommon "github.com/hashicorp/packer-plugin-azure/builder/azure/common" "github.com/hashicorp/packer-plugin-azure/builder/azure/common/constants" "github.com/hashicorp/packer-plugin-sdk/multistep" - packersdk "github.com/hashicorp/packer-plugin-sdk/packer" ) 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") + state.Put(constants.ArmSubscription, "testSubscription") + state.Put(constants.ArmResourceGroupName, "testResourceGroupName") config := &Config{ winrmCertificate: "testCertificateString", } - certKVStep := NewStepCertificateInKeyVault(&cli, ui, config, config.winrmCertificate) + certKVStep := &StepCertificateInKeyVault{ + say: func(message string) {}, + error: func(e error) {}, + set: func(ctx context.Context, id hashiSecretsSDK.SecretId) error { return nil }, + config: config, + certificate: config.winrmCertificate} + 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) { @@ -49,18 +45,22 @@ func TestNewStepCertificateInKeyVault_error(t *testing.T) { 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") + state.Put(constants.ArmSubscription, "testSubscription") + state.Put(constants.ArmResourceGroupName, "testResourceGroupName") config := &Config{ winrmCertificate: "testCertificateString", } - certKVStep := NewStepCertificateInKeyVault(&cli, ui, config, config.winrmCertificate) + certKVStep := &StepCertificateInKeyVault{ + say: func(message string) {}, + error: func(e error) {}, + set: func(ctx context.Context, id hashiSecretsSDK.SecretId) error { return fmt.Errorf("Unit test fail") }, + config: config, + certificate: config.winrmCertificate} + stepAction := certKVStep.Run(context.TODO(), state) if stepAction != multistep.ActionHalt { diff --git a/builder/azure/arm/step_create_resource_group.go b/builder/azure/arm/step_create_resource_group.go index 86e3d5fd..fbb1c87d 100644 --- a/builder/azure/arm/step_create_resource_group.go +++ b/builder/azure/arm/step_create_resource_group.go @@ -7,8 +7,10 @@ import ( "context" "errors" "fmt" + "time" - "github.com/Azure/azure-sdk-for-go/services/resources/mgmt/2018-02-01/resources" + "github.com/hashicorp/go-azure-helpers/resourcemanager/commonids" + hashiGroupsSDK "github.com/hashicorp/go-azure-sdk/resource-manager/resources/2022-09-01/resourcegroups" "github.com/hashicorp/packer-plugin-azure/builder/azure/common/constants" "github.com/hashicorp/packer-plugin-sdk/multistep" packersdk "github.com/hashicorp/packer-plugin-sdk/packer" @@ -16,10 +18,10 @@ import ( type StepCreateResourceGroup struct { client *AzureClient - create func(ctx context.Context, resourceGroupName string, location string, tags map[string]*string) error + create func(ctx context.Context, subscriptionId string, 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) + exists func(ctx context.Context, subscriptionId string, resourceGroupName string) (bool, error) } func NewStepCreateResourceGroup(client *AzureClient, ui packersdk.Ui) *StepCreateResourceGroup { @@ -34,10 +36,11 @@ func NewStepCreateResourceGroup(client *AzureClient, ui packersdk.Ui) *StepCreat 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, +func (s *StepCreateResourceGroup) createResourceGroup(ctx context.Context, subscriptionId string, resourceGroupName string, location string, tags map[string]string) error { + id := commonids.NewResourceGroupID(subscriptionId, resourceGroupName) + _, err := s.client.ResourceGroupsClient.CreateOrUpdate(ctx, id, hashiGroupsSDK.ResourceGroup{ + Location: location, + Tags: &tags, }) if err != nil { @@ -46,13 +49,17 @@ func (s *StepCreateResourceGroup) createResourceGroup(ctx context.Context, resou return err } -func (s *StepCreateResourceGroup) doesResourceGroupExist(ctx context.Context, resourceGroupName string) (bool, error) { - exists, err := s.client.GroupsClient.CheckExistence(ctx, resourceGroupName) +func (s *StepCreateResourceGroup) doesResourceGroupExist(ctx context.Context, subscriptionId string, resourceGroupName string) (bool, error) { + id := commonids.NewResourceGroupID(subscriptionId, resourceGroupName) + exists, err := s.client.ResourceGroupsClient.Get(ctx, id) if err != nil { + if exists.HttpResponse.StatusCode == 404 { + return false, nil + } s.say(s.client.LastError.Error()) } - return exists.Response.StatusCode != 404, err + return exists.HttpResponse.StatusCode != 404, nil } func (s *StepCreateResourceGroup) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { @@ -64,7 +71,7 @@ func (s *StepCreateResourceGroup) Run(ctx context.Context, state multistep.State var resourceGroupName = state.Get(constants.ArmResourceGroupName).(string) var location = state.Get(constants.ArmLocation).(string) - tags, ok := state.Get(constants.ArmTags).(map[string]*string) + tags, ok := state.Get(constants.ArmNewSDKTags).(map[string]string) if !ok { err := fmt.Errorf("failed to extract tags from state bag") state.Put(constants.Error, err) @@ -72,7 +79,8 @@ func (s *StepCreateResourceGroup) Run(ctx context.Context, state multistep.State return multistep.ActionHalt } - exists, err := s.exists(ctx, resourceGroupName) + subscriptionId := state.Get(constants.ArmSubscription).(string) + exists, err := s.exists(ctx, subscriptionId, resourceGroupName) if err != nil { return processStepResult(err, s.error, state) } @@ -94,9 +102,9 @@ func (s *StepCreateResourceGroup) Run(ctx context.Context, state multistep.State s.say(fmt.Sprintf(" -> Location : '%s'", location)) s.say(" -> Tags :") for k, v := range tags { - s.say(fmt.Sprintf(" ->> %s : %s", k, *v)) + s.say(fmt.Sprintf(" ->> %s : %s", k, v)) } - err = s.create(ctx, resourceGroupName, location, tags) + err = s.create(ctx, subscriptionId, resourceGroupName, location, tags) if err == nil { state.Put(constants.ArmIsResourceGroupCreated, true) } @@ -122,28 +130,34 @@ func (s *StepCreateResourceGroup) Cleanup(state multistep.StateBag) { return } - ctx := context.TODO() + ctx, cancelFunc := context.WithTimeout(context.Background(), time.Minute*10) + defer cancelFunc() + resourceGroupName := state.Get(constants.ArmResourceGroupName).(string) - if exists, err := s.exists(ctx, resourceGroupName); !exists || err != nil { + subscriptionId := state.Get(constants.ArmSubscription).(string) + if exists, err := s.exists(ctx, subscriptionId, 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) + id := commonids.NewResourceGroupID(subscriptionId, resourceGroupName) + if state.Get(constants.ArmAsyncResourceGroupDelete).(bool) { + _, deleteErr := s.client.ResourceGroupsClient.Delete(ctx, id, hashiGroupsSDK.DefaultDeleteOperationOptions()) + if deleteErr != nil { + ui.Error(fmt.Sprintf("Error deleting resource group. Please delete it manually.\n\n"+ + "Name: %s\n"+ + "Error: %s", resourceGroupName, deleteErr)) + return + } + s.say(fmt.Sprintf("\n Not waiting for Resource Group delete as requested by user. Resource Group Name is %s", resourceGroupName)) + } else { + err := s.client.ResourceGroupsClient.DeleteThenPoll(ctx, id, hashiGroupsSDK.DefaultDeleteOperationOptions()) + 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 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 index 31e1ce77..9b9640ad 100644 --- a/builder/azure/arm/step_create_resource_group_test.go +++ b/builder/azure/arm/step_create_resource_group_test.go @@ -25,10 +25,10 @@ func TestStepCreateResourceGroupShouldFailIfBothGroupNames(t *testing.T) { stateBag.Put(constants.ArmTags, tags) var testSubject = &StepCreateResourceGroup{ - create: func(context.Context, string, string, map[string]*string) error { return nil }, + create: func(context.Context, string, 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 }, + exists: func(context.Context, string, string) (bool, error) { return false, nil }, } var result = testSubject.Run(context.Background(), stateBag) if result != multistep.ActionHalt { @@ -42,12 +42,12 @@ func TestStepCreateResourceGroupShouldFailIfBothGroupNames(t *testing.T) { func TestStepCreateResourceGroupShouldFailIfCreateFails(t *testing.T) { var testSubject = &StepCreateResourceGroup{ - create: func(context.Context, string, string, map[string]*string) error { + create: func(context.Context, string, 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 }, + exists: func(context.Context, string, string) (bool, error) { return false, nil }, } stateBag := createTestStateBagStepCreateResourceGroup() @@ -64,10 +64,10 @@ func TestStepCreateResourceGroupShouldFailIfCreateFails(t *testing.T) { func TestStepCreateResourceGroupShouldFailIfExistsFails(t *testing.T) { var testSubject = &StepCreateResourceGroup{ - create: func(context.Context, string, string, map[string]*string) error { return nil }, + create: func(context.Context, string, 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") }, + exists: func(context.Context, string, string) (bool, error) { return false, errors.New("FAIL") }, } stateBag := createTestStateBagStepCreateResourceGroup() @@ -84,10 +84,10 @@ func TestStepCreateResourceGroupShouldFailIfExistsFails(t *testing.T) { func TestStepCreateResourceGroupShouldPassIfCreatePasses(t *testing.T) { var testSubject = &StepCreateResourceGroup{ - create: func(context.Context, string, string, map[string]*string) error { return nil }, + create: func(context.Context, string, 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 }, + exists: func(context.Context, string, string) (bool, error) { return false, nil }, } stateBag := createTestStateBagStepCreateResourceGroup() @@ -105,18 +105,20 @@ func TestStepCreateResourceGroupShouldPassIfCreatePasses(t *testing.T) { func TestStepCreateResourceGroupShouldTakeStepArgumentsFromStateBag(t *testing.T) { var actualResourceGroupName string var actualLocation string - var actualTags map[string]*string + var actualSubscriptionId string + var actualTags map[string]string var testSubject = &StepCreateResourceGroup{ - create: func(ctx context.Context, resourceGroupName string, location string, tags map[string]*string) error { + create: func(ctx context.Context, subscriptionId string, resourceGroupName string, location string, tags map[string]string) error { actualResourceGroupName = resourceGroupName actualLocation = location actualTags = tags + actualSubscriptionId = subscriptionId return nil }, say: func(message string) {}, error: func(e error) {}, - exists: func(context.Context, string) (bool, error) { return false, nil }, + exists: func(context.Context, string, string) (bool, error) { return false, nil }, } stateBag := createTestStateBagStepCreateResourceGroup() @@ -128,7 +130,8 @@ func TestStepCreateResourceGroupShouldTakeStepArgumentsFromStateBag(t *testing.T var expectedResourceGroupName = stateBag.Get(constants.ArmResourceGroupName).(string) var expectedLocation = stateBag.Get(constants.ArmLocation).(string) - var expectedTags = stateBag.Get(constants.ArmTags).(map[string]*string) + var expectedSubscription = stateBag.Get(constants.ArmSubscription).(string) + var expectedTags = stateBag.Get(constants.ArmNewSDKTags).(map[string]string) if actualResourceGroupName != expectedResourceGroupName { t.Fatal("Expected the step to source 'constants.ArmResourceGroupName' from the state bag, but it did not.") @@ -138,8 +141,12 @@ func TestStepCreateResourceGroupShouldTakeStepArgumentsFromStateBag(t *testing.T t.Fatal("Expected the step to source 'constants.ArmResourceGroupName' from the state bag, but it did not.") } + if actualSubscriptionId != expectedSubscription { + t.Fatal("Expected the step to source 'constants.ArmSubscription' from the state bag, but it did no") + } + 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.") + t.Fatal("Expected the step to source 'constants.ArmNewSDKTags' from the state bag, but it did not.") } _, ok := stateBag.GetOk(constants.ArmIsResourceGroupCreated) @@ -150,12 +157,12 @@ func TestStepCreateResourceGroupShouldTakeStepArgumentsFromStateBag(t *testing.T func TestStepCreateResourceGroupMarkShouldFailIfTryingExistingButDoesntExist(t *testing.T) { var testSubject = &StepCreateResourceGroup{ - create: func(context.Context, string, string, map[string]*string) error { + create: func(context.Context, string, 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 }, + exists: func(context.Context, string, string) (bool, error) { return false, nil }, } stateBag := createTestExistingStateBagStepCreateResourceGroup() @@ -172,12 +179,12 @@ func TestStepCreateResourceGroupMarkShouldFailIfTryingExistingButDoesntExist(t * func TestStepCreateResourceGroupMarkShouldFailIfTryingTempButExist(t *testing.T) { var testSubject = &StepCreateResourceGroup{ - create: func(context.Context, string, string, map[string]*string) error { + create: func(context.Context, string, 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 }, + exists: func(context.Context, string, string) (bool, error) { return true, nil }, } stateBag := createTestStateBagStepCreateResourceGroup() @@ -197,14 +204,15 @@ func createTestStateBagStepCreateResourceGroup() multistep.StateBag { stateBag.Put(constants.ArmLocation, "Unit Test: Location") stateBag.Put(constants.ArmResourceGroupName, "Unit Test: ResourceGroupName") + stateBag.Put(constants.ArmSubscription, "Unit Test: Subscription") stateBag.Put(constants.ArmIsExistingResourceGroup, false) value := "Unit Test: Tags" - tags := map[string]*string{ - "tag01": &value, + tags := map[string]string{ + "tag01": value, } - stateBag.Put(constants.ArmTags, tags) + stateBag.Put(constants.ArmNewSDKTags, tags) return stateBag } @@ -213,14 +221,15 @@ func createTestExistingStateBagStepCreateResourceGroup() multistep.StateBag { stateBag.Put(constants.ArmLocation, "Unit Test: Location") stateBag.Put(constants.ArmResourceGroupName, "Unit Test: ResourceGroupName") + stateBag.Put(constants.ArmSubscription, "Subscription") stateBag.Put(constants.ArmIsExistingResourceGroup, true) value := "Unit Test: Tags" - tags := map[string]*string{ - "tag01": &value, + tags := map[string]string{ + "tag01": value, } - stateBag.Put(constants.ArmTags, tags) + stateBag.Put(constants.ArmNewSDKTags, tags) return stateBag } @@ -232,16 +241,16 @@ func TestStepCreateResourceGroupShouldFailIfTagsFailCast(t *testing.T) { stateBag.Put(constants.ArmIsExistingResourceGroup, true) value := "Unit Test: Tags" - tags := map[string]string{ - "tag01": value, + tags := map[string]*string{ + "tag01": &value, } - stateBag.Put(constants.ArmTags, tags) + stateBag.Put(constants.ArmNewSDKTags, tags) var testSubject = &StepCreateResourceGroup{ - create: func(context.Context, string, string, map[string]*string) error { return nil }, + create: func(context.Context, string, 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 }, + exists: func(context.Context, string, string) (bool, error) { return false, nil }, } var result = testSubject.Run(context.Background(), stateBag) if result != multistep.ActionHalt { diff --git a/builder/azure/arm/step_deploy_template.go b/builder/azure/arm/step_deploy_template.go index 33fee981..62424323 100644 --- a/builder/azure/arm/step_deploy_template.go +++ b/builder/azure/arm/step_deploy_template.go @@ -12,10 +12,18 @@ import ( "sync" "time" + "github.com/hashicorp/go-azure-helpers/resourcemanager/commonids" + hashiVMSDK "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-01/virtualmachines" + hashiDisksSDK "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-02/disks" + hashiNetworkSecurityGroupsSDK "github.com/hashicorp/go-azure-sdk/resource-manager/network/2022-09-01/networksecuritygroups" + hashiVirtualNetworksSDK "github.com/hashicorp/go-azure-sdk/resource-manager/network/2022-09-01/virtualnetworks" + hashiDeploymentOperationsSDK "github.com/hashicorp/go-azure-sdk/resource-manager/resources/2022-09-01/deploymentoperations" + hashiDeploymentsSDK "github.com/hashicorp/go-azure-sdk/resource-manager/resources/2022-09-01/deployments" "github.com/hashicorp/packer-plugin-azure/builder/azure/common/constants" "github.com/hashicorp/packer-plugin-sdk/multistep" packersdk "github.com/hashicorp/packer-plugin-sdk/packer" "github.com/hashicorp/packer-plugin-sdk/retry" + giovanniBlobStorageSDK "github.com/tombuildsstuff/giovanni/storage/2020-08-04/blob/blobs" ) type DeploymentTemplateType int @@ -27,10 +35,10 @@ const ( 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, imageName string, resourceGroupName string, isManagedDisk bool) error + deploy func(ctx context.Context, subscriptionId string, resourceGroupName string, deploymentName string) error + delete func(ctx context.Context, subscriptionId, deploymentName, resourceGroupName string) error + disk func(ctx context.Context, subscriptionId string, resourceGroupName string, computeName string) (string, string, error) + deleteDisk func(ctx context.Context, imageName string, resourceGroupName string, isManagedDisk bool, subscriptionId string, storageAccountName string) error deleteDeployment func(ctx context.Context, state multistep.StateBag) error say func(message string) error func(e error) @@ -63,28 +71,32 @@ func (s *StepDeployTemplate) Run(ctx context.Context, state multistep.StateBag) s.say("Deploying deployment template ...") var resourceGroupName = state.Get(constants.ArmResourceGroupName).(string) + var subscriptionId = state.Get(constants.ArmSubscription).(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.deploy(ctx, subscriptionId, resourceGroupName, s.name), s.error, state) } func (s *StepDeployTemplate) Cleanup(state multistep.StateBag) { + ctx, cancel := context.WithTimeout(context.Background(), time.Minute*10) defer func() { - err := s.deleteDeployment(context.Background(), state) + err := s.deleteDeployment(ctx, state) if err != nil { s.say(err.Error()) } + cancel() }() ui := state.Get("ui").(packersdk.Ui) deploymentName := s.name resourceGroupName := state.Get(constants.ArmResourceGroupName).(string) + subscriptionId := state.Get(constants.ArmSubscription).(string) if s.templateType == KeyVaultTemplate { ui.Say("\nDeleting KeyVault created during build") - err := s.delete(context.TODO(), deploymentName, resourceGroupName) + err := s.delete(ctx, subscriptionId, deploymentName, resourceGroupName) if err != nil { s.reportIfError(err, resourceGroupName) } @@ -95,11 +107,13 @@ func (s *StepDeployTemplate) Cleanup(state multistep.StateBag) { // delete the disk as the image request will return a 404 computeName := state.Get(constants.ArmComputeName).(string) isManagedDisk := state.Get(constants.ArmIsManagedImage).(bool) - imageType, imageName, err := s.disk(context.TODO(), resourceGroupName, computeName) + isSIGImage := state.Get(constants.ArmIsSIGImage).(bool) + armStorageAccountName := state.Get(constants.ArmStorageAccountName).(string) + imageType, imageName, err := s.disk(ctx, subscriptionId, resourceGroupName, computeName) if err != nil { ui.Error(fmt.Sprintf("Could not retrieve OS Image details: %s", err)) } - err = s.delete(context.TODO(), deploymentName, resourceGroupName) + err = s.delete(ctx, subscriptionId, deploymentName, resourceGroupName) if err != nil { s.reportIfError(err, resourceGroupName) } @@ -112,7 +126,7 @@ func (s *StepDeployTemplate) Cleanup(state multistep.StateBag) { } if !state.Get(constants.ArmKeepOSDisk).(bool) { ui.Say(fmt.Sprintf(" Deleting -> %s : '%s'", imageType, imageName)) - err = s.deleteDisk(context.TODO(), imageName, resourceGroupName, isManagedDisk) + err = s.deleteDisk(ctx, imageName, resourceGroupName, (isManagedDisk || isSIGImage), subscriptionId, armStorageAccountName) if err != nil { ui.Error(fmt.Sprintf("Error deleting resource. Please delete manually.\n\n"+ "Name: %s\n"+ @@ -127,7 +141,7 @@ func (s *StepDeployTemplate) Cleanup(state multistep.StateBag) { for i, additionaldisk := range dataDisks { s.say(fmt.Sprintf(" Deleting Additional Disk -> %d: '%s'", i+1, additionaldisk)) - err := s.deleteImage(context.TODO(), additionaldisk, resourceGroupName, isManagedDisk) + err := s.deleteImage(ctx, additionaldisk, resourceGroupName, (isManagedDisk || isSIGImage), subscriptionId, armStorageAccountName) if err != nil { s.say("Failed to delete the managed Additional Disk!") } @@ -136,114 +150,111 @@ func (s *StepDeployTemplate) Cleanup(state multistep.StateBag) { } } -func (s *StepDeployTemplate) deployTemplate(ctx context.Context, resourceGroupName string, deploymentName string) error { +func (s *StepDeployTemplate) deployTemplate(ctx context.Context, subscriptionId string, 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) + id := hashiDeploymentsSDK.NewResourceGroupProviderDeploymentID(subscriptionId, resourceGroupName, deploymentName) + err = s.client.DeploymentsClient.CreateOrUpdateThenPoll(ctx, id, *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 + return nil } func (s *StepDeployTemplate) deleteDeploymentObject(ctx context.Context, state multistep.StateBag) error { deploymentName := s.name resourceGroupName := state.Get(constants.ArmResourceGroupName).(string) + subscriptionId := state.Get(constants.ArmSubscription).(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) + id := hashiDeploymentsSDK.NewResourceGroupProviderDeploymentID(subscriptionId, resourceGroupName, deploymentName) + err := s.client.DeploymentsClient.DeleteThenPoll(ctx, id) if err != nil { return err } - - return f.WaitForCompletionRef(ctx, s.client.DeploymentsClient.Client) + return nil } -func (s *StepDeployTemplate) getImageDetails(ctx context.Context, resourceGroupName string, computeName string) (string, string, error) { +func (s *StepDeployTemplate) getImageDetails(ctx context.Context, subscriptionId string, 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, "") + vmID := hashiVMSDK.NewVirtualMachineID(subscriptionId, resourceGroupName, computeName) + vm, err := s.client.VirtualMachinesClient.Get(ctx, vmID, hashiVMSDK.DefaultGetOperationOptions()) if err != nil { return imageName, imageType, err } - - if vm.StorageProfile.OsDisk.Vhd != nil { + if err != nil { + s.say(s.client.LastError.Error()) + return "", "", err + } + if model := vm.Model; model == nil { + return "", "", errors.New("TODO") + } + if vm.Model.Properties.StorageProfile.OsDisk.Vhd != nil { imageType = "image" - imageName = *vm.StorageProfile.OsDisk.Vhd.URI + imageName = *vm.Model.Properties.StorageProfile.OsDisk.Vhd.Uri return imageType, imageName, nil } - if vm.StorageProfile.OsDisk.ManagedDisk.ID == nil { + if vm.Model.Properties.StorageProfile.OsDisk.ManagedDisk.Id == nil { return "", "", fmt.Errorf("unable to obtain a OS disk for %q, please check that the instance has been created", computeName) } imageType = "Microsoft.Compute/disks" - imageName = *vm.StorageProfile.OsDisk.ManagedDisk.ID + imageName = *vm.Model.Properties.StorageProfile.OsDisk.ManagedDisk.Id return imageType, imageName, nil } -func deleteResource(ctx context.Context, client *AzureClient, resourceType string, resourceName string, resourceGroupName string) error { +func deleteResource(ctx context.Context, client *AzureClient, subscriptionId string, resourceType string, resourceName string, resourceGroupName string) error { switch resourceType { case "Microsoft.Compute/virtualMachines": - forcedelete := false - f, err := client.VirtualMachinesClient.Delete(ctx, resourceGroupName, resourceName, &forcedelete) - if err == nil { - err = f.WaitForCompletionRef(ctx, client.VirtualMachinesClient.Client) + vmID := hashiVMSDK.NewVirtualMachineID(subscriptionId, resourceGroupName, resourceName) + // TODO don't rely on default operations, set hard delete to false + if err := client.VirtualMachinesClient.DeleteThenPoll(ctx, vmID, hashiVMSDK.DefaultDeleteOperationOptions()); err != nil { + return err } - return err case "Microsoft.KeyVault/vaults": - _, err := client.VaultClientDelete.Delete(ctx, resourceGroupName, resourceName) + id := commonids.NewKeyVaultID(subscriptionId, resourceGroupName, resourceName) + _, err := client.VaultsClient.Delete(ctx, id) return err case "Microsoft.Network/networkInterfaces": - f, err := client.InterfacesClient.Delete(ctx, resourceGroupName, resourceName) - if err == nil { - err = f.WaitForCompletionRef(ctx, client.InterfacesClient.Client) - } + interfaceID := commonids.NewNetworkInterfaceID(subscriptionId, resourceGroupName, resourceName) + err := client.NetworkMetaClient.NetworkInterfaces.DeleteThenPoll(ctx, interfaceID) return err case "Microsoft.Network/virtualNetworks": - f, err := client.VirtualNetworksClient.Delete(ctx, resourceGroupName, resourceName) - if err == nil { - err = f.WaitForCompletionRef(ctx, client.VirtualNetworksClient.Client) - } + vnetID := hashiVirtualNetworksSDK.NewVirtualNetworkID(subscriptionId, resourceGroupName, resourceName) + err := client.NetworkMetaClient.VirtualNetworks.DeleteThenPoll(ctx, vnetID) return err case "Microsoft.Network/networkSecurityGroups": - f, err := client.SecurityGroupsClient.Delete(ctx, resourceGroupName, resourceName) - if err == nil { - err = f.WaitForCompletionRef(ctx, client.SecurityGroupsClient.Client) - } + secGroupId := hashiNetworkSecurityGroupsSDK.NewNetworkSecurityGroupID(subscriptionId, resourceGroupName, resourceName) + err := client.NetworkMetaClient.NetworkSecurityGroups.DeleteThenPoll(ctx, secGroupId) return err case "Microsoft.Network/publicIPAddresses": - f, err := client.PublicIPAddressesClient.Delete(ctx, resourceGroupName, resourceName) - if err == nil { - err = f.WaitForCompletionRef(ctx, client.PublicIPAddressesClient.Client) - } + ipID := commonids.NewPublicIPAddressID(subscriptionId, resourceGroupName, resourceName) + err := client.NetworkMetaClient.PublicIPAddresses.DeleteThenPoll(ctx, ipID) return err } return nil } -func (s *StepDeployTemplate) deleteImage(ctx context.Context, imageName string, resourceGroupName string, isManagedDisk bool) error { +// TODO Let's split this into two seperate methods, right now its confusing, especially with the changes I'm making +// deleteVHD and deleteManagedDisk, and then just check in Cleanup which function to call +func (s *StepDeployTemplate) deleteImage(ctx context.Context, imageName string, resourceGroupName string, isManagedDisk bool, subscriptionId string, storageAccountName string) error { // Managed disk if isManagedDisk { 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) + diskId := hashiDisksSDK.NewDiskID(subscriptionId, resourceGroupName, diskName) + + if err := s.client.DisksClient.DeleteThenPoll(ctx, diskId); err != nil { + return err } - return err + return nil } // VHD image @@ -252,25 +263,20 @@ func (s *StepDeployTemplate) deleteImage(ctx context.Context, imageName string, return err } xs := strings.Split(u.Path, "/") + var blobName = strings.Join(xs[2:], "/") 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) + _, err = s.client.GiovanniBlobClient.Delete(ctx, storageAccountName, "images", blobName, giovanniBlobStorageSDK.DeleteInput{}) + return err } -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) +func (s *StepDeployTemplate) deleteDeploymentResources(ctx context.Context, subscriptionId, deploymentName, resourceGroupName string) error { + var maxResources int64 = 50 + options := hashiDeploymentOperationsSDK.DefaultListOperationOptions() + options.Top = &maxResources + id := hashiDeploymentOperationsSDK.NewResourceGroupDeploymentID(subscriptionId, resourceGroupName, deploymentName) + deploymentOperations, err := s.client.DeploymentOperationsClient.ListComplete(ctx, id, options) if err != nil { s.reportIfError(err, resourceGroupName) return err @@ -278,11 +284,9 @@ func (s *StepDeployTemplate) deleteDeploymentResources(ctx context.Context, depl resources := map[string]string{} - for deploymentOperations.NotDone() { - deploymentOperation := deploymentOperations.Value() + for _, deploymentOperation := range deploymentOperations.Items { // Sometimes an empty operation is added to the list by Azure if deploymentOperation.Properties.TargetResource == nil { - _ = deploymentOperations.Next() continue } @@ -292,9 +296,6 @@ func (s *StepDeployTemplate) deleteDeploymentResources(ctx context.Context, depl 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 @@ -305,12 +306,13 @@ func (s *StepDeployTemplate) deleteDeploymentResources(ctx context.Context, depl defer wg.Done() retryConfig := retry.Config{ Tries: 10, - RetryDelay: (&retry.Backoff{InitialBackoff: 10 * time.Second, MaxBackoff: 600 * time.Second, Multiplier: 2}).Linear, + RetryDelay: (&retry.Backoff{InitialBackoff: 5 * time.Second, MaxBackoff: 60 * time.Second, Multiplier: 1.5}).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, + subscriptionId, resourceType, resourceName, resourceGroupName) diff --git a/builder/azure/arm/step_deploy_template_test.go b/builder/azure/arm/step_deploy_template_test.go index 416eb939..110dc200 100644 --- a/builder/azure/arm/step_deploy_template_test.go +++ b/builder/azure/arm/step_deploy_template_test.go @@ -15,7 +15,7 @@ import ( func TestStepDeployTemplateShouldFailIfDeployFails(t *testing.T) { var testSubject = &StepDeployTemplate{ - deploy: func(context.Context, string, string) error { + deploy: func(context.Context, string, string, string) error { return fmt.Errorf("!! Unit Test FAIL !!") }, say: func(message string) {}, @@ -25,6 +25,7 @@ func TestStepDeployTemplateShouldFailIfDeployFails(t *testing.T) { 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) } @@ -36,7 +37,7 @@ func TestStepDeployTemplateShouldFailIfDeployFails(t *testing.T) { func TestStepDeployTemplateShouldPassIfDeployPasses(t *testing.T) { var testSubject = &StepDeployTemplate{ - deploy: func(context.Context, string, string) error { return nil }, + deploy: func(context.Context, string, string, string) error { return nil }, say: func(message string) {}, error: func(e error) {}, } @@ -56,12 +57,13 @@ func TestStepDeployTemplateShouldPassIfDeployPasses(t *testing.T) { func TestStepDeployTemplateShouldTakeStepArgumentsFromStateBag(t *testing.T) { var actualResourceGroupName string var actualDeploymentName string + var actualSubscriptionId string var testSubject = &StepDeployTemplate{ - deploy: func(ctx context.Context, resourceGroupName string, deploymentName string) error { + deploy: func(ctx context.Context, subscriptionId string, resourceGroupName string, deploymentName string) error { actualResourceGroupName = resourceGroupName actualDeploymentName = deploymentName - + actualSubscriptionId = subscriptionId return nil }, say: func(message string) {}, @@ -78,6 +80,7 @@ func TestStepDeployTemplateShouldTakeStepArgumentsFromStateBag(t *testing.T) { } var expectedResourceGroupName = stateBag.Get(constants.ArmResourceGroupName).(string) + var expectedSubscriptionId = stateBag.Get(constants.ArmSubscription).(string) if actualDeploymentName != "--deployment-name--" { t.Fatal("Expected StepValidateTemplate to source 'constants.ArmDeploymentName' from the state bag, but it did not.") @@ -86,6 +89,10 @@ func TestStepDeployTemplateShouldTakeStepArgumentsFromStateBag(t *testing.T) { if actualResourceGroupName != expectedResourceGroupName { t.Fatal("Expected the step to source 'constants.ArmResourceGroupName' from the state bag, but it did not.") } + + if actualSubscriptionId != expectedSubscriptionId { + t.Fatal("Expected the step to source 'constants.ArmSubscription' from the state bag, but it did not.") + } } func TestStepDeployTemplateDeleteImageShouldFailWhenImageUrlCannotBeParsed(t *testing.T) { @@ -96,7 +103,7 @@ func TestStepDeployTemplateDeleteImageShouldFailWhenImageUrlCannotBeParsed(t *te templateType: VirtualMachineTemplate, } // Invalid URL per https://golang.org/src/net/url/url_test.go - err := testSubject.deleteImage(context.TODO(), "http://[fe80::1%en0]/", "Unit Test: ResourceGroupName", false) + err := testSubject.deleteImage(context.TODO(), "http://[fe80::1%en0]/", "Unit Test: ResourceGroupName", false, "subscriptionId", "") if err == nil { t.Fatal("Expected a failure because of the failed image name") } @@ -109,7 +116,7 @@ func TestStepDeployTemplateDeleteImageShouldFailWithInvalidImage(t *testing.T) { name: "--deployment-name--", templateType: VirtualMachineTemplate, } - err := testSubject.deleteImage(context.TODO(), "storage.blob.core.windows.net/abc", "Unit Test: ResourceGroupName", false) + err := testSubject.deleteImage(context.TODO(), "storage.blob.core.windows.net/abc", "Unit Test: ResourceGroupName", false, "subscriptionId", "") if err == nil { t.Fatal("Expected a failure because of the failed image name") } @@ -209,25 +216,27 @@ func createTestStateBagStepDeployTemplate() multistep.StateBag { stateBag := new(multistep.BasicStateBag) stateBag.Put(constants.ArmDeploymentName, "Unit Test: DeploymentName") + stateBag.Put(constants.ArmStorageAccountName, "Unit Test: StorageAccountName") stateBag.Put(constants.ArmResourceGroupName, "Unit Test: ResourceGroupName") stateBag.Put(constants.ArmComputeName, "Unit Test: ComputeName") + stateBag.Put(constants.ArmSubscription, "Unit Test: Subscription") return stateBag } func createTestStepDeployTemplateDeleteOSImage(deleteDiskCounter *int, templateType DeploymentTemplateType) *StepDeployTemplate { return &StepDeployTemplate{ - deploy: func(context.Context, string, string) error { return nil }, + deploy: func(context.Context, string, string, string) error { return nil }, say: func(message string) {}, error: func(e error) {}, - deleteDisk: func(ctx context.Context, imageName string, resourceGroupName string, isManagedDisk bool) error { + deleteDisk: func(ctx context.Context, imageName string, resourceGroupName string, isManagedDisk bool, subscriptionId string, storageAccountName string) error { *deleteDiskCounter++ return nil }, - disk: func(ctx context.Context, resourceGroupName, computeName string) (string, string, error) { + disk: func(ctx context.Context, subscriptionId, resourceGroupName, computeName string) (string, string, error) { return "Microsoft.Compute/disks", "", nil }, - delete: func(ctx context.Context, deploymentName, resourceGroupName string) error { + delete: func(ctx context.Context, subscriptionId, deploymentName, resourceGroupName string) error { return nil }, deleteDeployment: func(ctx context.Context, state multistep.StateBag) error { diff --git a/builder/azure/arm/step_get_additional_disks.go b/builder/azure/arm/step_get_additional_disks.go index 8c524278..46cb6055 100644 --- a/builder/azure/arm/step_get_additional_disks.go +++ b/builder/azure/arm/step_get_additional_disks.go @@ -5,10 +5,10 @@ package arm import ( "context" + "errors" "fmt" - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2021-11-01/compute" - + hashiVMSDK "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-01/virtualmachines" "github.com/hashicorp/packer-plugin-azure/builder/azure/common/constants" "github.com/hashicorp/packer-plugin-sdk/multistep" @@ -17,7 +17,7 @@ import ( type StepGetDataDisk struct { client *AzureClient - query func(ctx context.Context, resourceGroupName string, computeName string) (compute.VirtualMachine, error) + query func(ctx context.Context, subscriptionId string, resourceGroupName string, computeName string) (*hashiVMSDK.VirtualMachine, error) say func(message string) error func(e error) } @@ -33,12 +33,16 @@ func NewStepGetAdditionalDisks(client *AzureClient, ui packersdk.Ui) *StepGetDat 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, "") +func (s *StepGetDataDisk) queryCompute(ctx context.Context, subscriptionId string, resourceGroupName string, computeName string) (*hashiVMSDK.VirtualMachine, error) { + vmID := hashiVMSDK.NewVirtualMachineID(subscriptionId, resourceGroupName, computeName) + vm, err := s.client.VirtualMachinesClient.Get(ctx, vmID, hashiVMSDK.DefaultGetOperationOptions()) if err != nil { s.say(s.client.LastError.Error()) } - return vm, err + if model := vm.Model; model == nil { + return nil, errors.New("TODO") + } + return vm.Model, err } func (s *StepGetDataDisk) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { @@ -46,11 +50,11 @@ func (s *StepGetDataDisk) Run(ctx context.Context, state multistep.StateBag) mul var resourceGroupName = state.Get(constants.ArmResourceGroupName).(string) var computeName = state.Get(constants.ArmComputeName).(string) - + var subscriptionId = state.Get(constants.ArmSubscription).(string) s.say(fmt.Sprintf(" -> ResourceGroupName : '%s'", resourceGroupName)) s.say(fmt.Sprintf(" -> ComputeName : '%s'", computeName)) - vm, err := s.query(ctx, resourceGroupName, computeName) + vm, err := s.query(ctx, subscriptionId, resourceGroupName, computeName) if err != nil { state.Put(constants.Error, err) s.error(err) @@ -58,15 +62,15 @@ func (s *StepGetDataDisk) Run(ctx context.Context, state multistep.StateBag) mul return multistep.ActionHalt } - if vm.StorageProfile.DataDisks != nil { + if vm.Properties.StorageProfile.DataDisks != nil { var vhdUri string - additional_disks := make([]string, len(*vm.StorageProfile.DataDisks)) - for i, additionaldisk := range *vm.StorageProfile.DataDisks { + additional_disks := make([]string, len(*vm.Properties.StorageProfile.DataDisks)) + for i, additionaldisk := range *vm.Properties.StorageProfile.DataDisks { if additionaldisk.Vhd != nil { - vhdUri = *additionaldisk.Vhd.URI + vhdUri = *additionaldisk.Vhd.Uri s.say(fmt.Sprintf(" -> Additional Disk %d : '%s'", i+1, vhdUri)) } else { - vhdUri = *additionaldisk.ManagedDisk.ID + vhdUri = *additionaldisk.ManagedDisk.Id s.say(fmt.Sprintf(" -> Managed Additional Disk %d : '%s'", i+1, vhdUri)) } additional_disks[i] = vhdUri diff --git a/builder/azure/arm/step_get_additional_disks_test.go b/builder/azure/arm/step_get_additional_disks_test.go index 17ab145d..ce3806fc 100644 --- a/builder/azure/arm/step_get_additional_disks_test.go +++ b/builder/azure/arm/step_get_additional_disks_test.go @@ -8,8 +8,7 @@ import ( "fmt" "testing" - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2021-11-01/compute" - + hashiVMSDK "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-01/virtualmachines" "github.com/hashicorp/packer-plugin-azure/builder/azure/common/constants" "github.com/hashicorp/packer-plugin-sdk/multistep" @@ -17,7 +16,7 @@ import ( func TestStepGetAdditionalDiskShouldFailIfGetFails(t *testing.T) { var testSubject = &StepGetDataDisk{ - query: func(context.Context, string, string) (compute.VirtualMachine, error) { + query: func(context.Context, string, string, string) (*hashiVMSDK.VirtualMachine, error) { return createVirtualMachineWithDataDisksFromUri("test.vhd"), fmt.Errorf("!! Unit Test FAIL !!") }, say: func(message string) {}, @@ -38,7 +37,7 @@ func TestStepGetAdditionalDiskShouldFailIfGetFails(t *testing.T) { func TestStepGetAdditionalDiskShouldPassIfGetPasses(t *testing.T) { var testSubject = &StepGetDataDisk{ - query: func(context.Context, string, string) (compute.VirtualMachine, error) { + query: func(context.Context, string, string, string) (*hashiVMSDK.VirtualMachine, error) { return createVirtualMachineWithDataDisksFromUri("test.vhd"), nil }, say: func(message string) {}, @@ -60,12 +59,12 @@ func TestStepGetAdditionalDiskShouldPassIfGetPasses(t *testing.T) { func TestStepGetAdditionalDiskShouldTakeValidateArgumentsFromStateBag(t *testing.T) { var actualResourceGroupName string var actualComputeName string - + var actualSubscriptionId string var testSubject = &StepGetDataDisk{ - query: func(ctx context.Context, resourceGroupName string, computeName string) (compute.VirtualMachine, error) { + query: func(ctx context.Context, subscriptionId string, resourceGroupName string, computeName string) (*hashiVMSDK.VirtualMachine, error) { actualResourceGroupName = resourceGroupName actualComputeName = computeName - + actualSubscriptionId = subscriptionId return createVirtualMachineWithDataDisksFromUri("test.vhd"), nil }, say: func(message string) {}, @@ -82,6 +81,8 @@ func TestStepGetAdditionalDiskShouldTakeValidateArgumentsFromStateBag(t *testing var expectedComputeName = stateBag.Get(constants.ArmComputeName).(string) var expectedResourceGroupName = stateBag.Get(constants.ArmResourceGroupName).(string) + var expectedSubscriptionId = stateBag.Get(constants.ArmSubscription).(string) + if actualComputeName != expectedComputeName { t.Fatal("Expected the step to source 'constants.ArmResourceGroupName' from the state bag, but it did not.") } @@ -90,6 +91,9 @@ func TestStepGetAdditionalDiskShouldTakeValidateArgumentsFromStateBag(t *testing t.Fatal("Expected the step to source 'constants.ArmResourceGroupName' from the state bag, but it did not.") } + if actualSubscriptionId != expectedSubscriptionId { + t.Fatalf("Expected the step to source 'constants.ArmSubscriptionId' from the state bag, but it did not. %s %s", actualSubscriptionId, expectedSubscriptionId) + } 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) @@ -106,23 +110,24 @@ func createTestStateBagStepGetAdditionalDisks() multistep.StateBag { stateBag.Put(constants.ArmComputeName, "Unit Test: ComputeName") stateBag.Put(constants.ArmResourceGroupName, "Unit Test: ResourceGroupName") + stateBag.Put(constants.ArmSubscription, "Unit Test: Subscription") 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, +func createVirtualMachineWithDataDisksFromUri(vhdUri string) *hashiVMSDK.VirtualMachine { + vm := hashiVMSDK.VirtualMachine{ + Properties: &hashiVMSDK.VirtualMachineProperties{ + StorageProfile: &hashiVMSDK.StorageProfile{ + OsDisk: &hashiVMSDK.OSDisk{ + Vhd: &hashiVMSDK.VirtualHardDisk{ + Uri: &vhdUri, }, }, - DataDisks: &[]compute.DataDisk{ + DataDisks: &[]hashiVMSDK.DataDisk{ { - Vhd: &compute.VirtualHardDisk{ - URI: &vhdUri, + Vhd: &hashiVMSDK.VirtualHardDisk{ + Uri: &vhdUri, }, }, }, @@ -130,5 +135,5 @@ func createVirtualMachineWithDataDisksFromUri(vhdUri string) compute.VirtualMach }, } - return vm + return &vm } diff --git a/builder/azure/arm/step_get_certificate.go b/builder/azure/arm/step_get_certificate.go index 7ccc98d4..cecc38e3 100644 --- a/builder/azure/arm/step_get_certificate.go +++ b/builder/azure/arm/step_get_certificate.go @@ -5,9 +5,11 @@ package arm import ( "context" + "errors" "fmt" "time" + hashiSecretsSDK "github.com/hashicorp/go-azure-sdk/resource-manager/keyvault/2023-02-01/secrets" "github.com/hashicorp/packer-plugin-azure/builder/azure/common/constants" "github.com/hashicorp/packer-plugin-sdk/multistep" packersdk "github.com/hashicorp/packer-plugin-sdk/packer" @@ -15,7 +17,7 @@ import ( type StepGetCertificate struct { client *AzureClient - get func(keyVaultName string, secretName string) (string, error) + get func(ctx context.Context, subscriptionId, resourceGroupName string, keyVaultName string, secretName string) (string, error) say func(message string) error func(e error) pause func() @@ -33,20 +35,27 @@ func NewStepGetCertificate(client *AzureClient, ui packersdk.Ui) *StepGetCertifi return step } -func (s *StepGetCertificate) getCertificateUrl(keyVaultName string, secretName string) (string, error) { - secret, err := s.client.GetSecret(keyVaultName, secretName) +func (s *StepGetCertificate) getCertificateUrl(ctx context.Context, subscriptionId string, resourceGroupName string, keyVaultName string, secretName string) (string, error) { + id := hashiSecretsSDK.NewSecretID(subscriptionId, resourceGroupName, keyVaultName, secretName) + secret, err := s.client.SecretsClient.Get(ctx, id) if err != nil { s.say(s.client.LastError.Error()) return "", err } - return *secret.ID, err + if secret.Model == nil { + err = errors.New("TODO") + + } + return *secret.Model.Properties.SecretUriWithVersion, 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) + var resourceGroupName = state.Get(constants.ArmResourceGroupName).(string) + var subscriptionId = state.Get(constants.ArmSubscription).(string) s.say(fmt.Sprintf(" -> Key Vault Name : '%s'", keyVaultName)) s.say(fmt.Sprintf(" -> Key Vault Secret Name : '%s'", DefaultSecretName)) @@ -54,7 +63,7 @@ func (s *StepGetCertificate) Run(ctx context.Context, state multistep.StateBag) var err error var url string for i := 0; i < 5; i++ { - url, err = s.get(keyVaultName, DefaultSecretName) + url, err = s.get(ctx, subscriptionId, resourceGroupName, keyVaultName, DefaultSecretName) if err == nil { break } diff --git a/builder/azure/arm/step_get_certificate_test.go b/builder/azure/arm/step_get_certificate_test.go index fb9717f9..4c8dd012 100644 --- a/builder/azure/arm/step_get_certificate_test.go +++ b/builder/azure/arm/step_get_certificate_test.go @@ -14,7 +14,9 @@ import ( func TestStepGetCertificateShouldFailIfGetFails(t *testing.T) { var testSubject = &StepGetCertificate{ - get: func(string, string) (string, error) { return "", fmt.Errorf("!! Unit Test FAIL !!") }, + get: func(context.Context, string, string, string, string) (string, error) { + return "", fmt.Errorf("!! Unit Test FAIL !!") + }, say: func(message string) {}, error: func(e error) {}, pause: func() {}, @@ -34,7 +36,7 @@ func TestStepGetCertificateShouldFailIfGetFails(t *testing.T) { func TestStepGetCertificateShouldPassIfGetPasses(t *testing.T) { var testSubject = &StepGetCertificate{ - get: func(string, string) (string, error) { return "", nil }, + get: func(context.Context, string, string, string, string) (string, error) { return "", nil }, say: func(message string) {}, error: func(e error) {}, pause: func() {}, @@ -57,7 +59,7 @@ func TestStepGetCertificateShouldTakeStepArgumentsFromStateBag(t *testing.T) { var actualSecretName string var testSubject = &StepGetCertificate{ - get: func(keyVaultName string, secretName string) (string, error) { + get: func(ctx context.Context, subscriptionId string, resourceGroupName string, keyVaultName string, secretName string) (string, error) { actualKeyVaultName = keyVaultName actualSecretName = secretName @@ -97,6 +99,7 @@ func TestStepGetCertificateShouldTakeStepArgumentsFromStateBag(t *testing.T) { func createTestStateBagStepGetCertificate() multistep.StateBag { stateBag := new(multistep.BasicStateBag) stateBag.Put(constants.ArmKeyVaultName, "Unit Test: KeyVaultName") - + stateBag.Put(constants.ArmSubscription, "testSubscription") + stateBag.Put(constants.ArmResourceGroupName, "testResourceGroupName") return stateBag } diff --git a/builder/azure/arm/step_get_ip_address.go b/builder/azure/arm/step_get_ip_address.go index d84876bd..0b410d02 100644 --- a/builder/azure/arm/step_get_ip_address.go +++ b/builder/azure/arm/step_get_ip_address.go @@ -7,6 +7,9 @@ import ( "context" "fmt" + "github.com/hashicorp/go-azure-helpers/resourcemanager/commonids" + hashiNetworkInterfacesSDK "github.com/hashicorp/go-azure-sdk/resource-manager/network/2022-09-01/networkinterfaces" + hashiPublicIPSDK "github.com/hashicorp/go-azure-sdk/resource-manager/network/2022-09-01/publicipaddresses" "github.com/hashicorp/packer-plugin-azure/builder/azure/common/constants" "github.com/hashicorp/packer-plugin-sdk/multistep" packersdk "github.com/hashicorp/packer-plugin-sdk/packer" @@ -31,7 +34,7 @@ var ( type StepGetIPAddress struct { client *AzureClient endpoint EndpointType - get func(ctx context.Context, resourceGroupName string, ipAddressName string, interfaceName string) (string, error) + get func(ctx context.Context, subscriptionId string, resourceGroupName string, ipAddressName string, interfaceName string) (string, error) say func(message string) error func(e error) } @@ -56,29 +59,32 @@ func NewStepGetIPAddress(client *AzureClient, ui packersdk.Ui, endpoint Endpoint 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, "") +func (s *StepGetIPAddress) getPrivateIP(ctx context.Context, subscriptionId string, resourceGroupName string, ipAddressName string, interfaceName string) (string, error) { + intID := commonids.NewNetworkInterfaceID(subscriptionId, resourceGroupName, interfaceName) + resp, err := s.client.NetworkMetaClient.NetworkInterfaces.Get(ctx, intID, hashiNetworkInterfacesSDK.DefaultGetOperationOptions()) if err != nil { s.say(s.client.LastError.Error()) return "", err } - return *(*resp.IPConfigurations)[0].PrivateIPAddress, nil + return *(*resp.Model.Properties.IPConfigurations)[0].Properties.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, "") +func (s *StepGetIPAddress) getPublicIP(ctx context.Context, subscriptionId string, resourceGroupName string, ipAddressName string, interfaceName string) (string, error) { + ipID := commonids.NewPublicIPAddressID(subscriptionId, resourceGroupName, ipAddressName) + resp, err := s.client.NetworkMetaClient.PublicIPAddresses.Get(ctx, ipID, hashiPublicIPSDK.DefaultGetOperationOptions()) if err != nil { return "", err } - return *resp.IPAddress, nil + return *resp.Model.Properties.IPAddress, nil } -func (s *StepGetIPAddress) getPublicIPInPrivateNetwork(ctx context.Context, resourceGroupName string, ipAddressName string, interfaceName string) (string, error) { - // TODO: This was being called without capturing any return variables, causing linter issue, as far as I can tell calling getPrivateIP here does nothing, look into removing - _, _ = s.getPrivateIP(ctx, resourceGroupName, ipAddressName, interfaceName) - return s.getPublicIP(ctx, resourceGroupName, ipAddressName, interfaceName) +// TODO The interface name passed into getPublicIP has never done anything +// This code has been around for over 6 years so I'm hesistant to change it without more investigation so we should +// open a seperate GitHub issue for this when looking to merge the SDK branch +func (s *StepGetIPAddress) getPublicIPInPrivateNetwork(ctx context.Context, subscriptionId string, resourceGroupName string, ipAddressName string, interfaceName string) (string, error) { + return s.getPublicIP(ctx, subscriptionId, resourceGroupName, ipAddressName, interfaceName) } func (s *StepGetIPAddress) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { @@ -86,6 +92,7 @@ func (s *StepGetIPAddress) Run(ctx context.Context, state multistep.StateBag) mu var resourceGroupName = state.Get(constants.ArmResourceGroupName).(string) var ipAddressName = state.Get(constants.ArmPublicIPAddressName).(string) + var subscriptionId = state.Get(constants.ArmSubscription).(string) var nicName = state.Get(constants.ArmNicName).(string) s.say(fmt.Sprintf(" -> ResourceGroupName : '%s'", resourceGroupName)) @@ -93,7 +100,7 @@ func (s *StepGetIPAddress) Run(ctx context.Context, state multistep.StateBag) mu 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) + address, err := s.get(ctx, subscriptionId, resourceGroupName, ipAddressName, nicName) if err != nil { state.Put(constants.Error, err) s.error(err) diff --git a/builder/azure/arm/step_get_ip_address_test.go b/builder/azure/arm/step_get_ip_address_test.go index 1b777331..47cc3008 100644 --- a/builder/azure/arm/step_get_ip_address_test.go +++ b/builder/azure/arm/step_get_ip_address_test.go @@ -17,7 +17,7 @@ func TestStepGetIPAddressShouldFailIfGetFails(t *testing.T) { for _, endpoint := range endpoints { var testSubject = &StepGetIPAddress{ - get: func(context.Context, string, string, string) (string, error) { + get: func(context.Context, string, string, string, string) (string, error) { return "", fmt.Errorf("!! Unit Test FAIL !!") }, endpoint: endpoint, @@ -43,7 +43,7 @@ func TestStepGetIPAddressShouldPassIfGetPasses(t *testing.T) { for _, endpoint := range endpoints { var testSubject = &StepGetIPAddress{ - get: func(context.Context, string, string, string) (string, error) { return "", nil }, + get: func(context.Context, string, string, string, string) (string, error) { return "", nil }, endpoint: endpoint, say: func(message string) {}, error: func(e error) {}, @@ -66,15 +66,16 @@ func TestStepGetIPAddressShouldTakeStepArgumentsFromStateBag(t *testing.T) { var actualResourceGroupName string var actualIPAddressName string var actualNicName string + var actualSubscriptionID 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) { + get: func(ctx context.Context, subscriptionID string, resourceGroupName string, ipAddressName string, nicName string) (string, error) { actualResourceGroupName = resourceGroupName actualIPAddressName = ipAddressName actualNicName = nicName - + actualSubscriptionID = subscriptionID return "127.0.0.1", nil }, endpoint: endpoint, @@ -92,6 +93,7 @@ func TestStepGetIPAddressShouldTakeStepArgumentsFromStateBag(t *testing.T) { var expectedResourceGroupName = stateBag.Get(constants.ArmResourceGroupName).(string) var expectedIPAddressName = stateBag.Get(constants.ArmPublicIPAddressName).(string) var expectedNicName = stateBag.Get(constants.ArmNicName).(string) + var expectedSubscriptionID = stateBag.Get(constants.ArmSubscription).(string) if actualIPAddressName != expectedIPAddressName { t.Fatal("Expected StepGetIPAddress to source 'constants.ArmIPAddressName' from the state bag, but it did not.") @@ -104,7 +106,9 @@ func TestStepGetIPAddressShouldTakeStepArgumentsFromStateBag(t *testing.T) { if actualNicName != expectedNicName { t.Fatalf("Expected StepGetIPAddress to source 'constants.ArmNetworkInterfaceName' from the state bag, but it did not.") } - + if actualSubscriptionID != expectedSubscriptionID { + 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) @@ -122,6 +126,7 @@ func createTestStateBagStepGetIPAddress() multistep.StateBag { stateBag.Put(constants.ArmPublicIPAddressName, "Unit Test: PublicIPAddressName") stateBag.Put(constants.ArmNicName, "Unit Test: NicName") stateBag.Put(constants.ArmResourceGroupName, "Unit Test: ResourceGroupName") + stateBag.Put(constants.ArmSubscription, "Unit Test: SubscriptionID") return stateBag } diff --git a/builder/azure/arm/step_get_os_disk.go b/builder/azure/arm/step_get_os_disk.go index 86ad3ed4..fed69479 100644 --- a/builder/azure/arm/step_get_os_disk.go +++ b/builder/azure/arm/step_get_os_disk.go @@ -5,10 +5,10 @@ package arm import ( "context" + "errors" "fmt" - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2021-11-01/compute" - + hashiVMSDK "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-01/virtualmachines" "github.com/hashicorp/packer-plugin-azure/builder/azure/common/constants" "github.com/hashicorp/packer-plugin-sdk/multistep" @@ -17,7 +17,7 @@ import ( type StepGetOSDisk struct { client *AzureClient - query func(ctx context.Context, resourceGroupName string, computeName string) (compute.VirtualMachine, error) + query func(ctx context.Context, resourceGroupName string, computeName string, subscriptionId string) (*hashiVMSDK.VirtualMachine, error) say func(message string) error func(e error) } @@ -33,12 +33,17 @@ func NewStepGetOSDisk(client *AzureClient, ui packersdk.Ui) *StepGetOSDisk { 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, "") +func (s *StepGetOSDisk) queryCompute(ctx context.Context, resourceGroupName string, computeName string, subscriptionId string) (*hashiVMSDK.VirtualMachine, error) { + vmID := hashiVMSDK.NewVirtualMachineID(subscriptionId, resourceGroupName, computeName) + vm, err := s.client.VirtualMachinesClient.Get(ctx, vmID, hashiVMSDK.DefaultGetOperationOptions()) if err != nil { s.say(s.client.LastError.Error()) + return nil, err + } + if model := vm.Model; model != nil { + return model, nil } - return vm, err + return nil, errors.New("TODO") } func (s *StepGetOSDisk) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { @@ -46,11 +51,12 @@ func (s *StepGetOSDisk) Run(ctx context.Context, state multistep.StateBag) multi var resourceGroupName = state.Get(constants.ArmResourceGroupName).(string) var computeName = state.Get(constants.ArmComputeName).(string) + var subscriptionId = state.Get(constants.ArmSubscription).(string) s.say(fmt.Sprintf(" -> ResourceGroupName : '%s'", resourceGroupName)) s.say(fmt.Sprintf(" -> ComputeName : '%s'", computeName)) - vm, err := s.query(ctx, resourceGroupName, computeName) + vm, err := s.query(ctx, resourceGroupName, computeName, subscriptionId) if err != nil { state.Put(constants.Error, err) s.error(err) @@ -59,11 +65,11 @@ func (s *StepGetOSDisk) Run(ctx context.Context, state multistep.StateBag) multi } var vhdUri string - if vm.StorageProfile.OsDisk.Vhd != nil { - vhdUri = *vm.StorageProfile.OsDisk.Vhd.URI + if vm.Properties.StorageProfile.OsDisk.Vhd != nil { + vhdUri = *vm.Properties.StorageProfile.OsDisk.Vhd.Uri s.say(fmt.Sprintf(" -> OS Disk : '%s'", vhdUri)) } else { - vhdUri = *vm.StorageProfile.OsDisk.ManagedDisk.ID + vhdUri = *vm.Properties.StorageProfile.OsDisk.ManagedDisk.Id s.say(fmt.Sprintf(" -> Managed OS Disk : '%s'", vhdUri)) } diff --git a/builder/azure/arm/step_get_os_disk_test.go b/builder/azure/arm/step_get_os_disk_test.go index 9ecca974..4b25db12 100644 --- a/builder/azure/arm/step_get_os_disk_test.go +++ b/builder/azure/arm/step_get_os_disk_test.go @@ -8,8 +8,7 @@ import ( "fmt" "testing" - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2021-11-01/compute" - + hashiVMSDK "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-01/virtualmachines" "github.com/hashicorp/packer-plugin-azure/builder/azure/common/constants" "github.com/hashicorp/packer-plugin-sdk/multistep" @@ -17,7 +16,7 @@ import ( func TestStepGetOSDiskShouldFailIfGetFails(t *testing.T) { var testSubject = &StepGetOSDisk{ - query: func(context.Context, string, string) (compute.VirtualMachine, error) { + query: func(context.Context, string, string, string) (*hashiVMSDK.VirtualMachine, error) { return createVirtualMachineFromUri("test.vhd"), fmt.Errorf("!! Unit Test FAIL !!") }, say: func(message string) {}, @@ -38,7 +37,7 @@ func TestStepGetOSDiskShouldFailIfGetFails(t *testing.T) { func TestStepGetOSDiskShouldPassIfGetPasses(t *testing.T) { var testSubject = &StepGetOSDisk{ - query: func(context.Context, string, string) (compute.VirtualMachine, error) { + query: func(context.Context, string, string, string) (*hashiVMSDK.VirtualMachine, error) { return createVirtualMachineFromUri("test.vhd"), nil }, say: func(message string) {}, @@ -60,12 +59,13 @@ func TestStepGetOSDiskShouldPassIfGetPasses(t *testing.T) { func TestStepGetOSDiskShouldTakeValidateArgumentsFromStateBag(t *testing.T) { var actualResourceGroupName string var actualComputeName string + var actualSubscriptionId string var testSubject = &StepGetOSDisk{ - query: func(ctx context.Context, resourceGroupName string, computeName string) (compute.VirtualMachine, error) { + query: func(ctx context.Context, resourceGroupName string, computeName string, subscriptionId string) (*hashiVMSDK.VirtualMachine, error) { actualResourceGroupName = resourceGroupName actualComputeName = computeName - + actualSubscriptionId = subscriptionId return createVirtualMachineFromUri("test.vhd"), nil }, say: func(message string) {}, @@ -81,6 +81,7 @@ func TestStepGetOSDiskShouldTakeValidateArgumentsFromStateBag(t *testing.T) { var expectedComputeName = stateBag.Get(constants.ArmComputeName).(string) var expectedResourceGroupName = stateBag.Get(constants.ArmResourceGroupName).(string) + var expectedSubscriptionId = stateBag.Get(constants.ArmSubscription).(string) if actualComputeName != expectedComputeName { t.Fatal("Expected the step to source 'constants.ArmResourceGroupName' from the state bag, but it did not.") @@ -90,6 +91,10 @@ func TestStepGetOSDiskShouldTakeValidateArgumentsFromStateBag(t *testing.T) { t.Fatal("Expected the step to source 'constants.ArmResourceGroupName' from the state bag, but it did not.") } + if actualSubscriptionId != expectedSubscriptionId { + 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) @@ -105,22 +110,23 @@ func createTestStateBagStepGetOSDisk() multistep.StateBag { stateBag.Put(constants.ArmComputeName, "Unit Test: ComputeName") stateBag.Put(constants.ArmResourceGroupName, "Unit Test: ResourceGroupName") + stateBag.Put(constants.ArmSubscription, "Unit Test: Subscription") 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, +func createVirtualMachineFromUri(vhdUri string) *hashiVMSDK.VirtualMachine { + vm := hashiVMSDK.VirtualMachine{ + Properties: &hashiVMSDK.VirtualMachineProperties{ + StorageProfile: &hashiVMSDK.StorageProfile{ + OsDisk: &hashiVMSDK.OSDisk{ + Vhd: &hashiVMSDK.VirtualHardDisk{ + Uri: &vhdUri, }, }, }, }, } - return vm + return &vm } diff --git a/builder/azure/arm/step_get_source_image_name.go b/builder/azure/arm/step_get_source_image_name.go index fba8705c..03f42113 100644 --- a/builder/azure/arm/step_get_source_image_name.go +++ b/builder/azure/arm/step_get_source_image_name.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package arm import ( @@ -9,7 +6,7 @@ import ( "log" "regexp" - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2021-11-01/compute" + hashiGalleryImageVersionsSDK "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-03/galleryimageversions" "github.com/hashicorp/packer-plugin-sdk/multistep" packersdk "github.com/hashicorp/packer-plugin-sdk/packer" "github.com/hashicorp/packer-plugin-sdk/packerbuilderdata" @@ -19,7 +16,7 @@ type StepGetSourceImageName struct { client *AzureClient config *Config GeneratedData *packerbuilderdata.GeneratedData - getGalleryVersion func(context.Context) (compute.GalleryImageVersion, error) + getGalleryVersion func(context.Context) (*hashiGalleryImageVersionsSDK.GalleryImageVersion, error) say func(message string) error func(e error) } @@ -60,16 +57,15 @@ func (s *StepGetSourceImageName) Run(ctx context.Context, state multistep.StateB return multistep.ActionContinue } - if image.GalleryImageVersionProperties != nil && image.GalleryImageVersionProperties.StorageProfile != nil && - image.GalleryImageVersionProperties.StorageProfile.Source != nil && image.GalleryImageVersionProperties.StorageProfile.Source.ID != nil { + if image.Properties != nil && + image.Properties.StorageProfile.Source != nil && image.Properties.StorageProfile.Source.Id != nil { // Shared Image Galleries can be created in two different ways // Either directly from a VM (in the builder this means not setting managed_image_name), for these types of images we set the artifact ID as the Gallery Image ID // Or through an intermediate managed image. in which case we use that managed image as the artifact ID. // First check if the parent Gallery Image Version source ID is a managed image, if so we use that as our source image name - parentSourceID := *image.GalleryImageVersionProperties.StorageProfile.Source.ID - + parentSourceID := *image.Properties.StorageProfile.Source.Id isSIGSourcedFromManagedImage, _ := regexp.MatchString("/subscriptions/[^/]*/resourceGroups/[^/]*/providers/Microsoft.Compute/images/[^/]*$", parentSourceID) if isSIGSourcedFromManagedImage { @@ -78,8 +74,8 @@ func (s *StepGetSourceImageName) Run(ctx context.Context, state multistep.StateB return multistep.ActionContinue } else { // If the Gallery Image Version was not sourced from a Managed Image, that means it was captured directly from a VM, so we just use the gallery ID itself as the source image - s.say(fmt.Sprintf(" -> SourceImageName: '%s'", *image.ID)) - s.GeneratedData.Put("SourceImageName", *image.ID) + s.say(fmt.Sprintf(" -> SourceImageName: '%s'", *image.Id)) + s.GeneratedData.Put("SourceImageName", *image.Id) return multistep.ActionContinue } @@ -103,12 +99,15 @@ func (s *StepGetSourceImageName) Run(ctx context.Context, state multistep.StateB return multistep.ActionContinue } -func (s *StepGetSourceImageName) GetGalleryImageVersion(ctx context.Context) (compute.GalleryImageVersion, error) { +func (s *StepGetSourceImageName) GetGalleryImageVersion(ctx context.Context) (*hashiGalleryImageVersionsSDK.GalleryImageVersion, error) { client := s.client.GalleryImageVersionsClient - client.SubscriptionID = s.config.SharedGallery.Subscription - return client.Get(ctx, s.config.SharedGallery.ResourceGroup, - s.config.SharedGallery.GalleryName, s.config.SharedGallery.ImageName, s.config.SharedGallery.ImageVersion, "") + galleryVersionId := hashiGalleryImageVersionsSDK.NewImageVersionID(s.config.SharedGallery.Subscription, s.config.SharedGallery.ResourceGroup, s.config.SharedGallery.GalleryName, s.config.SharedGallery.ImageName, s.config.SharedGallery.ImageVersion) + result, err := client.Get(ctx, galleryVersionId, hashiGalleryImageVersionsSDK.DefaultGetOperationOptions()) + if err != nil { + return nil, err + } + return result.Model, nil } func (*StepGetSourceImageName) Cleanup(multistep.StateBag) { diff --git a/builder/azure/arm/step_get_source_image_name_test.go b/builder/azure/arm/step_get_source_image_name_test.go index 80795d26..4ebc5469 100644 --- a/builder/azure/arm/step_get_source_image_name_test.go +++ b/builder/azure/arm/step_get_source_image_name_test.go @@ -8,7 +8,7 @@ import ( "context" "testing" - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2021-11-01/compute" + hashiGalleryImageVersionsSDK "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-03/galleryimageversions" "github.com/hashicorp/packer-plugin-azure/builder/azure/common/client" "github.com/hashicorp/packer-plugin-sdk/multistep" packersdk "github.com/hashicorp/packer-plugin-sdk/packer" @@ -25,23 +25,23 @@ func TestStepGetSourceImageName(t *testing.T) { vmSourcedSigID := "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/pkr-Resource-Group-blah/providers/Microsoft.Compute/virtualMachines/pkrvmexample" managedImageSourcedSigID := "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/pkr-Resource-Group-blah/providers/Microsoft.Compute/images/exampleimage" sigArtifactID := "example-sig-id" - vmSourcedSigImageVersion := &compute.GalleryImageVersion{ - ID: &sigArtifactID, - GalleryImageVersionProperties: &compute.GalleryImageVersionProperties{ - StorageProfile: &compute.GalleryImageVersionStorageProfile{ - Source: &compute.GalleryArtifactVersionSource{ - ID: &vmSourcedSigID, + vmSourcedSigImageVersion := &hashiGalleryImageVersionsSDK.GalleryImageVersion{ + Id: &sigArtifactID, + Properties: &hashiGalleryImageVersionsSDK.GalleryImageVersionProperties{ + StorageProfile: hashiGalleryImageVersionsSDK.GalleryImageVersionStorageProfile{ + Source: &hashiGalleryImageVersionsSDK.GalleryArtifactVersionFullSource{ + Id: &vmSourcedSigID, }, }, }, } - managedImageSourcedSigImageVersion := &compute.GalleryImageVersion{ - ID: &sigArtifactID, - GalleryImageVersionProperties: &compute.GalleryImageVersionProperties{ - StorageProfile: &compute.GalleryImageVersionStorageProfile{ - Source: &compute.GalleryArtifactVersionSource{ - ID: &managedImageSourcedSigID, + managedImageSourcedSigImageVersion := &hashiGalleryImageVersionsSDK.GalleryImageVersion{ + Id: &sigArtifactID, + Properties: &hashiGalleryImageVersionsSDK.GalleryImageVersionProperties{ + StorageProfile: hashiGalleryImageVersionsSDK.GalleryImageVersionStorageProfile{ + Source: &hashiGalleryImageVersionsSDK.GalleryArtifactVersionFullSource{ + Id: &managedImageSourcedSigID, }, }, }, @@ -51,7 +51,7 @@ func TestStepGetSourceImageName(t *testing.T) { name string config *Config expected string - mockedGalleryImage *compute.GalleryImageVersion + mockedGalleryImage *hashiGalleryImageVersionsSDK.GalleryImageVersion }{ { name: "ImageUrl", @@ -115,11 +115,13 @@ func TestStepGetSourceImageName(t *testing.T) { } if tt.mockedGalleryImage != nil { step = StepGetSourceImageName{ - config: tt.config, - GeneratedData: &genData, - say: ui.Say, - error: func(e error) {}, - getGalleryVersion: func(ctx context.Context) (compute.GalleryImageVersion, error) { return *tt.mockedGalleryImage, nil }, + config: tt.config, + GeneratedData: &genData, + say: ui.Say, + error: func(e error) {}, + getGalleryVersion: func(ctx context.Context) (*hashiGalleryImageVersionsSDK.GalleryImageVersion, error) { + return tt.mockedGalleryImage, nil + }, } } step.Run(context.TODO(), state) diff --git a/builder/azure/arm/step_power_off_compute.go b/builder/azure/arm/step_power_off_compute.go index c04d7c58..646ba5dd 100644 --- a/builder/azure/arm/step_power_off_compute.go +++ b/builder/azure/arm/step_power_off_compute.go @@ -7,6 +7,7 @@ import ( "context" "fmt" + hashiVMSDK "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-01/virtualmachines" "github.com/hashicorp/packer-plugin-azure/builder/azure/common/constants" "github.com/hashicorp/packer-plugin-sdk/multistep" packersdk "github.com/hashicorp/packer-plugin-sdk/packer" @@ -14,7 +15,7 @@ import ( type StepPowerOffCompute struct { client *AzureClient - powerOff func(ctx context.Context, resourceGroupName string, computeName string) error + powerOff func(ctx context.Context, subscriptionId string, resourceGroupName string, computeName string) error say func(message string) error func(e error) } @@ -30,12 +31,10 @@ func NewStepPowerOffCompute(client *AzureClient, ui packersdk.Ui) *StepPowerOffC return step } -func (s *StepPowerOffCompute) powerOffCompute(ctx context.Context, resourceGroupName string, computeName string) error { +func (s *StepPowerOffCompute) powerOffCompute(ctx context.Context, subscriptionId string, resourceGroupName string, computeName string) error { hibernate := false - f, err := s.client.VirtualMachinesClient.Deallocate(ctx, resourceGroupName, computeName, &hibernate) - if err == nil { - err = f.WaitForCompletionRef(ctx, s.client.VirtualMachinesClient.Client) - } + vmId := hashiVMSDK.NewVirtualMachineID(subscriptionId, resourceGroupName, computeName) + err := s.client.VirtualMachinesClient.DeallocateThenPoll(ctx, vmId, hashiVMSDK.DeallocateOperationOptions{Hibernate: &hibernate}) if err != nil { s.say(s.client.LastError.Error()) } @@ -47,11 +46,12 @@ func (s *StepPowerOffCompute) Run(ctx context.Context, state multistep.StateBag) var resourceGroupName = state.Get(constants.ArmResourceGroupName).(string) var computeName = state.Get(constants.ArmComputeName).(string) + var subscriptionId = state.Get(constants.ArmSubscription).(string) s.say(fmt.Sprintf(" -> ResourceGroupName : '%s'", resourceGroupName)) s.say(fmt.Sprintf(" -> ComputeName : '%s'", computeName)) - err := s.powerOff(ctx, resourceGroupName, computeName) + err := s.powerOff(ctx, subscriptionId, resourceGroupName, computeName) return processStepResult(err, s.error, state) } diff --git a/builder/azure/arm/step_power_off_compute_test.go b/builder/azure/arm/step_power_off_compute_test.go index efe04c1c..b33072fb 100644 --- a/builder/azure/arm/step_power_off_compute_test.go +++ b/builder/azure/arm/step_power_off_compute_test.go @@ -14,7 +14,7 @@ import ( func TestStepPowerOffComputeShouldFailIfPowerOffFails(t *testing.T) { var testSubject = &StepPowerOffCompute{ - powerOff: func(context.Context, string, string) error { return fmt.Errorf("!! Unit Test FAIL !!") }, + powerOff: func(context.Context, string, string, string) error { return fmt.Errorf("!! Unit Test FAIL !!") }, say: func(message string) {}, error: func(e error) {}, } @@ -33,7 +33,7 @@ func TestStepPowerOffComputeShouldFailIfPowerOffFails(t *testing.T) { func TestStepPowerOffComputeShouldPassIfPowerOffPasses(t *testing.T) { var testSubject = &StepPowerOffCompute{ - powerOff: func(context.Context, string, string) error { return nil }, + powerOff: func(context.Context, string, string, string) error { return nil }, say: func(message string) {}, error: func(e error) {}, } @@ -53,11 +53,13 @@ func TestStepPowerOffComputeShouldPassIfPowerOffPasses(t *testing.T) { func TestStepPowerOffComputeShouldTakeStepArgumentsFromStateBag(t *testing.T) { var actualResourceGroupName string var actualComputeName string + var actualSubscriptionId string var testSubject = &StepPowerOffCompute{ - powerOff: func(ctx context.Context, resourceGroupName string, computeName string) error { + powerOff: func(ctx context.Context, subscriptionId string, resourceGroupName string, computeName string) error { actualResourceGroupName = resourceGroupName actualComputeName = computeName + actualSubscriptionId = subscriptionId return nil }, @@ -74,6 +76,7 @@ func TestStepPowerOffComputeShouldTakeStepArgumentsFromStateBag(t *testing.T) { var expectedComputeName = stateBag.Get(constants.ArmComputeName).(string) var expectedResourceGroupName = stateBag.Get(constants.ArmResourceGroupName).(string) + var expectedSubscriptionId = stateBag.Get(constants.ArmSubscription).(string) if actualComputeName != expectedComputeName { t.Fatal("Expected the step to source 'constants.ArmResourceGroupName' from the state bag, but it did not.") @@ -82,6 +85,10 @@ func TestStepPowerOffComputeShouldTakeStepArgumentsFromStateBag(t *testing.T) { if actualResourceGroupName != expectedResourceGroupName { t.Fatal("Expected the step to source 'constants.ArmResourceGroupName' from the state bag, but it did not.") } + + if actualSubscriptionId != expectedSubscriptionId { + t.Fatal("Expected the step to source 'constants.ArmSubscription' from the state bag, but it did not.") + } } func createTestStateBagStepPowerOffCompute() multistep.StateBag { @@ -89,6 +96,6 @@ func createTestStateBagStepPowerOffCompute() multistep.StateBag { stateBag.Put(constants.ArmComputeName, "Unit Test: ComputeName") stateBag.Put(constants.ArmResourceGroupName, "Unit Test: ResourceGroupName") - + stateBag.Put(constants.ArmSubscription, "UnitTest: SubscriptionId") 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 index 2ae1e488..6cf3adeb 100644 --- a/builder/azure/arm/step_publish_to_shared_image_gallery.go +++ b/builder/azure/arm/step_publish_to_shared_image_gallery.go @@ -7,8 +7,8 @@ import ( "context" "fmt" - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2021-11-01/compute" - "github.com/Azure/go-autorest/autorest/date" + hashiImagesSDK "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-01/images" + hashiGalleryImageVersionsSDK "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-03/galleryimageversions" "github.com/hashicorp/packer-plugin-azure/builder/azure/common/constants" "github.com/hashicorp/packer-plugin-sdk/multistep" packersdk "github.com/hashicorp/packer-plugin-sdk/packer" @@ -16,7 +16,7 @@ import ( type StepPublishToSharedImageGallery struct { client *AzureClient - publish func(ctx context.Context, sourceID string, sharedImageGallery SharedImageGalleryDestination, miSGImageVersionEndOfLifeDate string, miSGImageVersionExcludeFromLatest bool, miSigReplicaCount int32, location string, diskEncryptionSetId string, tags map[string]*string) (string, error) + publish func(ctx context.Context, subscriptionID string, sourceID string, sharedImageGallery SharedImageGalleryDestination, miSGImageVersionEndOfLifeDate string, miSGImageVersionExcludeFromLatest bool, miSigReplicaCount int64, location string, diskEncryptionSetId string, tags map[string]string) (string, error) say func(message string) error func(e error) toSIG func() bool @@ -40,13 +40,13 @@ func NewStepPublishToSharedImageGallery(client *AzureClient, ui packersdk.Ui, co return step } -func getSigDestinationStorageAccountType(s string) (compute.StorageAccountType, error) { +func getSigDestinationStorageAccountType(s string) (hashiGalleryImageVersionsSDK.StorageAccountType, error) { if s == "" { - return compute.StorageAccountTypeStandardLRS, nil + return hashiGalleryImageVersionsSDK.StorageAccountTypeStandardLRS, nil } - for _, t := range compute.PossibleStorageAccountTypeValues() { + for _, t := range hashiGalleryImageVersionsSDK.PossibleValuesForStorageAccountType() { if string(t) == s { - return t, nil + return hashiGalleryImageVersionsSDK.StorageAccountType(t), nil } } return "", fmt.Errorf("not an accepted value for shared_image_gallery_destination.storage_account_type") @@ -72,23 +72,11 @@ func getSigDestination(state multistep.StateBag) SharedImageGalleryDestination { } } -func (s *StepPublishToSharedImageGallery) publishToSig(ctx context.Context, sourceID string, sharedImageGallery SharedImageGalleryDestination, miSGImageVersionEndOfLifeDate string, miSGImageVersionExcludeFromLatest bool, miSigReplicaCount int32, location string, diskEncryptionSetId string, tags map[string]*string) (string, error) { - replicationRegions := make([]compute.TargetRegion, len(sharedImageGallery.SigDestinationReplicationRegions)) +func (s *StepPublishToSharedImageGallery) publishToSig(ctx context.Context, subscriptionID string, sourceID string, sharedImageGallery SharedImageGalleryDestination, miSGImageVersionEndOfLifeDate string, miSGImageVersionExcludeFromLatest bool, miSigReplicaCount int64, location string, diskEncryptionSetId string, tags map[string]string) (string, error) { + replicationRegions := make([]hashiGalleryImageVersionsSDK.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) + replicationRegions[i] = hashiGalleryImageVersionsSDK.TargetRegion{Name: regionName} } storageAccountType, err := getSigDestinationStorageAccountType(sharedImageGallery.SigDestinationStorageAccountType) @@ -99,55 +87,49 @@ func (s *StepPublishToSharedImageGallery) publishToSig(ctx context.Context, sour if diskEncryptionSetId != "" { for index, targetRegion := range replicationRegions { - targetRegion.Encryption = &compute.EncryptionImages{ - OsDiskImage: &compute.OSDiskImageEncryption{ - DiskEncryptionSetID: &diskEncryptionSetId, + targetRegion.Encryption = &hashiGalleryImageVersionsSDK.EncryptionImages{ + OsDiskImage: &hashiGalleryImageVersionsSDK.OSDiskImageEncryption{ + DiskEncryptionSetId: &diskEncryptionSetId, }, } replicationRegions[index] = targetRegion } } - galleryImageVersion := compute.GalleryImageVersion{ - Location: &location, - Tags: tags, - GalleryImageVersionProperties: &compute.GalleryImageVersionProperties{ - StorageProfile: &compute.GalleryImageVersionStorageProfile{ - Source: &compute.GalleryArtifactVersionSource{ - ID: &sourceID, + galleryImageVersion := hashiGalleryImageVersionsSDK.GalleryImageVersion{ + Location: location, + Tags: &tags, + Properties: &hashiGalleryImageVersionsSDK.GalleryImageVersionProperties{ + StorageProfile: hashiGalleryImageVersionsSDK.GalleryImageVersionStorageProfile{ + Source: &hashiGalleryImageVersionsSDK.GalleryArtifactVersionFullSource{ + Id: &sourceID, }, }, - PublishingProfile: &compute.GalleryImageVersionPublishingProfile{ + PublishingProfile: &hashiGalleryImageVersionsSDK.GalleryArtifactPublishingProfileBase{ TargetRegions: &replicationRegions, - EndOfLifeDate: endOfLifeDate, + EndOfLifeDate: &miSGImageVersionEndOfLifeDate, ExcludeFromLatest: &miSGImageVersionExcludeFromLatest, ReplicaCount: &miSigReplicaCount, - StorageAccountType: storageAccountType, + 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) + galleryImageVersionId := hashiGalleryImageVersionsSDK.NewImageVersionID(subscriptionID, sharedImageGallery.SigDestinationResourceGroup, sharedImageGallery.SigDestinationGalleryName, sharedImageGallery.SigDestinationImageName, sharedImageGallery.SigDestinationImageVersion) + err = s.client.GalleryImageVersionsClient.CreateOrUpdateThenPoll(ctx, galleryImageVersionId, galleryImageVersion) if err != nil { s.say(s.client.LastError.Error()) return "", err } - createdSGImageVersion, err := f.Result(s.client.GalleryImageVersionsClient) + createdSGImageVersion, err := s.client.GalleryImageVersionsClient.Get(ctx, galleryImageVersionId, hashiGalleryImageVersionsSDK.DefaultGetOperationOptions()) 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 + s.say(fmt.Sprintf(" -> Shared Gallery Image Version ID : '%s'", *(createdSGImageVersion.Model.Id))) + return *(createdSGImageVersion.Model.Id), nil } func (s *StepPublishToSharedImageGallery) Run(ctx context.Context, stateBag multistep.StateBag) multistep.StepAction { @@ -158,7 +140,7 @@ func (s *StepPublishToSharedImageGallery) Run(ctx context.Context, stateBag mult s.say("Publishing to Shared Image Gallery ...") location := stateBag.Get(constants.ArmLocation).(string) - tags := stateBag.Get(constants.ArmTags).(map[string]*string) + tags := stateBag.Get(constants.ArmNewSDKTags).(map[string]string) sharedImageGallery := getSigDestination(stateBag) var sourceID string @@ -171,13 +153,13 @@ func (s *StepPublishToSharedImageGallery) Run(ctx context.Context, stateBag mult managedImageSubscription := stateBag.Get(constants.ArmManagedImageSubscription).(string) sourceID = fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Compute/images/%s", managedImageSubscription, targetManagedImageResourceGroupName, targetManagedImageName) } else { - var imageParameters = stateBag.Get(constants.ArmImageParameters).(*compute.Image) - sourceID = *imageParameters.SourceVirtualMachine.ID + var imageParameters = stateBag.Get(constants.ArmImageParameters).(*hashiImagesSDK.Image) + sourceID = *imageParameters.Properties.SourceVirtualMachine.Id } miSGImageVersionEndOfLifeDate, _ := stateBag.Get(constants.ArmManagedImageSharedGalleryImageVersionEndOfLifeDate).(string) miSGImageVersionExcludeFromLatest, _ := stateBag.Get(constants.ArmManagedImageSharedGalleryImageVersionExcludeFromLatest).(bool) - miSigReplicaCount, _ := stateBag.Get(constants.ArmManagedImageSharedGalleryImageVersionReplicaCount).(int32) + miSigReplicaCount, _ := stateBag.Get(constants.ArmManagedImageSharedGalleryImageVersionReplicaCount).(int64) // Replica count must be between 1 and 100 inclusive if miSigReplicaCount <= 0 { miSigReplicaCount = constants.SharedImageGalleryImageVersionDefaultMinReplicaCount @@ -204,7 +186,8 @@ func (s *StepPublishToSharedImageGallery) Run(ctx context.Context, stateBag mult s.say(fmt.Sprintf(" -> SIG image version exclude from latest : '%t'", miSGImageVersionExcludeFromLatest)) s.say(fmt.Sprintf(" -> SIG replica count [1, 100] : '%d'", miSigReplicaCount)) - createdGalleryImageVersionID, err := s.publish(ctx, sourceID, sharedImageGallery, miSGImageVersionEndOfLifeDate, miSGImageVersionExcludeFromLatest, miSigReplicaCount, location, diskEncryptionSetId, tags) + subscriptionID := stateBag.Get(constants.ArmSharedImageGalleryDestinationSubscription).(string) + createdGalleryImageVersionID, err := s.publish(ctx, subscriptionID, sourceID, sharedImageGallery, miSGImageVersionEndOfLifeDate, miSGImageVersionExcludeFromLatest, miSigReplicaCount, location, diskEncryptionSetId, tags) if err != nil { stateBag.Put(constants.Error, err) 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 index b11cce88..92375b30 100644 --- a/builder/azure/arm/step_publish_to_shared_image_gallery_test.go +++ b/builder/azure/arm/step_publish_to_shared_image_gallery_test.go @@ -7,16 +7,16 @@ import ( "context" "testing" - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2021-11-01/compute" - "github.com/Azure/go-autorest/autorest/to" + hashiImagesSDK "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-01/images" + "github.com/hashicorp/packer-plugin-azure/builder/azure/common" "github.com/hashicorp/packer-plugin-azure/builder/azure/common/constants" "github.com/hashicorp/packer-plugin-sdk/multistep" ) func TestStepPublishToSharedImageGalleryShouldNotPublishForVhd(t *testing.T) { var testSubject = &StepPublishToSharedImageGallery{ - publish: func(context.Context, string, SharedImageGalleryDestination, string, bool, int32, string, string, map[string]*string) (string, error) { + publish: func(context.Context, string, string, SharedImageGalleryDestination, string, bool, int64, string, string, map[string]string) (string, error) { return "test", nil }, say: func(message string) {}, @@ -37,7 +37,7 @@ func TestStepPublishToSharedImageGalleryShouldNotPublishForVhd(t *testing.T) { func TestStepPublishToSharedImageGalleryShouldPublishForManagedImageWithSig(t *testing.T) { var testSubject = &StepPublishToSharedImageGallery{ - publish: func(context.Context, string, SharedImageGalleryDestination, string, bool, int32, string, string, map[string]*string) (string, error) { + publish: func(context.Context, string, string, SharedImageGalleryDestination, string, bool, int64, string, string, map[string]string) (string, error) { return "", nil }, say: func(message string) {}, @@ -58,7 +58,7 @@ func TestStepPublishToSharedImageGalleryShouldPublishForManagedImageWithSig(t *t func TestStepPublishToSharedImageGalleryShouldPublishForNonManagedImageWithSig(t *testing.T) { var testSubject = &StepPublishToSharedImageGallery{ - publish: func(context.Context, string, SharedImageGalleryDestination, string, bool, int32, string, string, map[string]*string) (string, error) { + publish: func(context.Context, string, string, SharedImageGalleryDestination, string, bool, int64, string, string, map[string]string) (string, error) { return "", nil }, say: func(message string) {}, @@ -86,21 +86,22 @@ func createTestStateBagStepPublishToSharedImageGallery(managed bool) multistep.S stateBag.Put(constants.ArmManagedImageSharedGalleryImageVersion, "Unit Test: ManagedImageSharedGalleryImageVersion") stateBag.Put(constants.ArmLocation, "Unit Test: Location") value := "Unit Test: Tags" - tags := map[string]*string{ - "tag01": &value, + tags := map[string]string{ + "tag01": value, } - stateBag.Put(constants.ArmTags, tags) + stateBag.Put(constants.ArmNewSDKTags, tags) stateBag.Put(constants.ArmManagedImageSharedGalleryReplicationRegions, []string{"ManagedImageSharedGalleryReplicationRegionA", "ManagedImageSharedGalleryReplicationRegionB"}) stateBag.Put(constants.ArmManagedImageSharedGalleryImageVersionStorageAccountType, "Standard_LRS") if managed { stateBag.Put(constants.ArmManagedImageResourceGroupName, "Unit Test: ManagedImageResourceGroupName") stateBag.Put(constants.ArmManagedImageName, "Unit Test: ManagedImageName") } else { - stateBag.Put(constants.ArmImageParameters, &compute.Image{ImageProperties: &compute.ImageProperties{ - SourceVirtualMachine: &compute.SubResource{ID: to.StringPtr("Unit Test: VM ID")}, + stateBag.Put(constants.ArmImageParameters, &hashiImagesSDK.Image{Properties: &hashiImagesSDK.ImageProperties{ + SourceVirtualMachine: &hashiImagesSDK.SubResource{Id: common.StringPtr("Unit Test: VM ID")}, }}) } stateBag.Put(constants.ArmManagedImageSubscription, "Unit Test: ManagedImageSubscription") + stateBag.Put(constants.ArmSharedImageGalleryDestinationSubscription, "Unit Test: ManagedImageSubscription") stateBag.Put(constants.ArmIsManagedImage, managed) stateBag.Put(constants.ArmIsSIGImage, true) diff --git a/builder/azure/arm/step_snapshot_data_disks.go b/builder/azure/arm/step_snapshot_data_disks.go index 1e94c586..bcb8b553 100644 --- a/builder/azure/arm/step_snapshot_data_disks.go +++ b/builder/azure/arm/step_snapshot_data_disks.go @@ -8,8 +8,8 @@ import ( "fmt" "strconv" - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2021-11-01/compute" - "github.com/Azure/go-autorest/autorest/to" + hashiSnapshotsSDK "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-02/snapshots" + "github.com/hashicorp/packer-plugin-azure/builder/azure/common" "github.com/hashicorp/packer-plugin-azure/builder/azure/common/constants" "github.com/hashicorp/packer-plugin-sdk/multistep" packersdk "github.com/hashicorp/packer-plugin-sdk/packer" @@ -17,7 +17,7 @@ import ( type StepSnapshotDataDisks struct { client *AzureClient - create func(ctx context.Context, resourceGroupName string, srcUriVhd string, location string, tags map[string]*string, dstSnapshotName string) error + create func(ctx context.Context, subscriptionId string, resourceGroupName string, srcUriVhd string, location string, tags map[string]string, dstSnapshotName string) error say func(message string) error func(e error) enable func() bool @@ -35,41 +35,32 @@ func NewStepSnapshotDataDisks(client *AzureClient, ui packersdk.Ui, config *Conf 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{ - SnapshotProperties: &compute.SnapshotProperties{ - CreationData: &compute.CreationData{ - CreateOption: compute.DiskCreateOptionCopy, - SourceResourceID: to.StringPtr(srcUriVhd), +func (s *StepSnapshotDataDisks) createDataDiskSnapshot(ctx context.Context, subscriptionId string, resourceGroupName string, srcUriVhd string, location string, tags map[string]string, dstSnapshotName string) error { + srcVhdToSnapshot := hashiSnapshotsSDK.Snapshot{ + Properties: &hashiSnapshotsSDK.SnapshotProperties{ + CreationData: hashiSnapshotsSDK.CreationData{ + CreateOption: hashiSnapshotsSDK.DiskCreateOptionCopy, + SourceResourceId: common.StringPtr(srcUriVhd), }, }, - Location: to.StringPtr(location), - Tags: tags, + Location: *common.StringPtr(location), + Tags: &tags, } - - f, err := s.client.SnapshotsClient.CreateOrUpdate(ctx, resourceGroupName, dstSnapshotName, srcVhdToSnapshot) + id := hashiSnapshotsSDK.NewSnapshotID(subscriptionId, resourceGroupName, dstSnapshotName) + err := s.client.SnapshotsClient.CreateOrUpdateThenPoll(ctx, id, 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) - + snapshot, err := s.client.SnapshotsClient.Get(ctx, id) if err != nil { s.say(s.client.LastError.Error()) return err } - s.say(fmt.Sprintf(" -> Snapshot ID : '%s'", *(createdSnapshot.ID))) + s.say(fmt.Sprintf(" -> Snapshot ID : '%s'", *(snapshot.Model.Id))) return nil } @@ -80,9 +71,10 @@ func (s *StepSnapshotDataDisks) Run(ctx context.Context, stateBag multistep.Stat var resourceGroupName = stateBag.Get(constants.ArmManagedImageResourceGroupName).(string) var location = stateBag.Get(constants.ArmLocation).(string) - var tags = stateBag.Get(constants.ArmTags).(map[string]*string) + var tags = stateBag.Get(constants.ArmNewSDKTags).(map[string]string) var additionalDisks = stateBag.Get(constants.ArmAdditionalDiskVhds).([]string) var dstSnapshotPrefix = stateBag.Get(constants.ArmManagedImageDataDiskSnapshotPrefix).(string) + var subscriptionId = stateBag.Get(constants.ArmSubscription).(string) s.say("Snapshotting data disk(s) ...") @@ -90,7 +82,7 @@ func (s *StepSnapshotDataDisks) Run(ctx context.Context, stateBag multistep.Stat s.say(fmt.Sprintf(" -> Data Disk : '%s'", disk)) dstSnapshotName := dstSnapshotPrefix + strconv.Itoa(i) - err := s.create(ctx, resourceGroupName, disk, location, tags, dstSnapshotName) + err := s.create(ctx, subscriptionId, resourceGroupName, disk, location, tags, dstSnapshotName) if err != nil { stateBag.Put(constants.Error, err) diff --git a/builder/azure/arm/step_snapshot_data_disks_test.go b/builder/azure/arm/step_snapshot_data_disks_test.go index 4b16beeb..46803a67 100644 --- a/builder/azure/arm/step_snapshot_data_disks_test.go +++ b/builder/azure/arm/step_snapshot_data_disks_test.go @@ -14,7 +14,7 @@ import ( func TestStepSnapshotDataDisksShouldFailIfSnapshotFails(t *testing.T) { var testSubject = &StepSnapshotDataDisks{ - create: func(context.Context, string, string, string, map[string]*string, string) error { + create: func(context.Context, string, string, string, string, map[string]string, string) error { return fmt.Errorf("!! Unit Test FAIL !!") }, say: func(message string) {}, @@ -36,7 +36,7 @@ func TestStepSnapshotDataDisksShouldFailIfSnapshotFails(t *testing.T) { func TestStepSnapshotDataDisksShouldNotExecute(t *testing.T) { var testSubject = &StepSnapshotDataDisks{ - create: func(context.Context, string, string, string, map[string]*string, string) error { + create: func(context.Context, string, string, string, string, map[string]string, string) error { return fmt.Errorf("!! Unit Test FAIL !!") }, say: func(message string) {}, @@ -52,7 +52,7 @@ func TestStepSnapshotDataDisksShouldNotExecute(t *testing.T) { func TestStepSnapshotDataDisksShouldPassIfSnapshotPasses(t *testing.T) { var testSubject = &StepSnapshotDataDisks{ - create: func(context.Context, string, string, string, map[string]*string, string) error { + create: func(context.Context, string, string, string, string, map[string]string, string) error { return nil }, say: func(message string) {}, @@ -82,10 +82,15 @@ func createTestStateBagStepSnapshotDataDisks() multistep.StateBag { tags := map[string]*string{ "tag01": &value, } + newSDKTags := map[string]string{ + "tag02:": value, + } stateBag.Put(constants.ArmTags, tags) + stateBag.Put(constants.ArmNewSDKTags, newSDKTags) stateBag.Put(constants.ArmAdditionalDiskVhds, []string{"subscriptions/123-456-789/resourceGroups/existingresourcegroup/providers/Microsoft.Compute/disks/osdisk"}) stateBag.Put(constants.ArmManagedImageDataDiskSnapshotPrefix, "Unit Test: ManagedImageDataDiskSnapshotPrefix") + stateBag.Put(constants.ArmSubscription, "Unit Test: SubscriptionId") return stateBag } diff --git a/builder/azure/arm/step_snapshot_os_disk.go b/builder/azure/arm/step_snapshot_os_disk.go index d802797d..54678ac1 100644 --- a/builder/azure/arm/step_snapshot_os_disk.go +++ b/builder/azure/arm/step_snapshot_os_disk.go @@ -7,8 +7,8 @@ import ( "context" "fmt" - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2021-11-01/compute" - "github.com/Azure/go-autorest/autorest/to" + hashiSnapshotsSDK "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-02/snapshots" + "github.com/hashicorp/packer-plugin-azure/builder/azure/common" "github.com/hashicorp/packer-plugin-azure/builder/azure/common/constants" "github.com/hashicorp/packer-plugin-sdk/multistep" packersdk "github.com/hashicorp/packer-plugin-sdk/packer" @@ -16,7 +16,7 @@ import ( type StepSnapshotOSDisk struct { client *AzureClient - create func(ctx context.Context, resourceGroupName string, srcUriVhd string, location string, tags map[string]*string, dstSnapshotName string) error + create func(ctx context.Context, subscriptionId string, resourceGroupName string, srcUriVhd string, location string, tags map[string]string, dstSnapshotName string) error say func(message string) error func(e error) enable func() bool @@ -34,41 +34,33 @@ func NewStepSnapshotOSDisk(client *AzureClient, ui packersdk.Ui, config *Config) return step } -func (s *StepSnapshotOSDisk) createSnapshot(ctx context.Context, resourceGroupName string, srcUriVhd string, location string, tags map[string]*string, dstSnapshotName string) error { +func (s *StepSnapshotOSDisk) createSnapshot(ctx context.Context, subscriptionId string, resourceGroupName string, srcUriVhd string, location string, tags map[string]string, dstSnapshotName string) error { - srcVhdToSnapshot := compute.Snapshot{ - SnapshotProperties: &compute.SnapshotProperties{ - CreationData: &compute.CreationData{ - CreateOption: compute.DiskCreateOptionCopy, - SourceResourceID: to.StringPtr(srcUriVhd), + srcVhdToSnapshot := hashiSnapshotsSDK.Snapshot{ + Properties: &hashiSnapshotsSDK.SnapshotProperties{ + CreationData: hashiSnapshotsSDK.CreationData{ + CreateOption: hashiSnapshotsSDK.DiskCreateOptionCopy, + SourceResourceId: common.StringPtr(srcUriVhd), }, }, - Location: to.StringPtr(location), - Tags: tags, + Location: *common.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) + id := hashiSnapshotsSDK.NewSnapshotID(subscriptionId, resourceGroupName, dstSnapshotName) + err := s.client.SnapshotsClient.CreateOrUpdateThenPoll(ctx, id, srcVhdToSnapshot) if err != nil { s.say(s.client.LastError.Error()) return err } - createdSnapshot, err := f.Result(s.client.SnapshotsClient) - + snapshot, err := s.client.SnapshotsClient.Get(ctx, id) if err != nil { s.say(s.client.LastError.Error()) return err } - s.say(fmt.Sprintf(" -> Snapshot ID : '%s'", *(createdSnapshot.ID))) + s.say(fmt.Sprintf(" -> Snapshot ID : '%s'", *(snapshot.Model.Id))) return nil } @@ -81,12 +73,13 @@ func (s *StepSnapshotOSDisk) Run(ctx context.Context, stateBag multistep.StateBa var resourceGroupName = stateBag.Get(constants.ArmManagedImageResourceGroupName).(string) var location = stateBag.Get(constants.ArmLocation).(string) - var tags = stateBag.Get(constants.ArmTags).(map[string]*string) + var tags = stateBag.Get(constants.ArmNewSDKTags).(map[string]string) var srcUriVhd = stateBag.Get(constants.ArmOSDiskVhd).(string) var dstSnapshotName = stateBag.Get(constants.ArmManagedImageOSDiskSnapshotName).(string) + var subscriptionId = stateBag.Get(constants.ArmSubscription).(string) s.say(fmt.Sprintf(" -> OS Disk : '%s'", srcUriVhd)) - err := s.create(ctx, resourceGroupName, srcUriVhd, location, tags, dstSnapshotName) + err := s.create(ctx, subscriptionId, resourceGroupName, srcUriVhd, location, tags, dstSnapshotName) if err != nil { stateBag.Put(constants.Error, err) diff --git a/builder/azure/arm/step_snapshot_os_disk_test.go b/builder/azure/arm/step_snapshot_os_disk_test.go index b358592f..9bb279f5 100644 --- a/builder/azure/arm/step_snapshot_os_disk_test.go +++ b/builder/azure/arm/step_snapshot_os_disk_test.go @@ -14,7 +14,7 @@ import ( func TestStepSnapshotOSDiskShouldFailIfSnapshotFails(t *testing.T) { var testSubject = &StepSnapshotOSDisk{ - create: func(context.Context, string, string, string, map[string]*string, string) error { + create: func(context.Context, string, string, string, string, map[string]string, string) error { return fmt.Errorf("!! Unit Test FAIL !!") }, say: func(message string) {}, @@ -36,7 +36,7 @@ func TestStepSnapshotOSDiskShouldFailIfSnapshotFails(t *testing.T) { func TestStepSnapshotOSDiskShouldNotExecute(t *testing.T) { var testSubject = &StepSnapshotOSDisk{ - create: func(context.Context, string, string, string, map[string]*string, string) error { + create: func(context.Context, string, string, string, string, map[string]string, string) error { return fmt.Errorf("!! Unit Test FAIL !!") }, say: func(message string) {}, @@ -52,7 +52,7 @@ func TestStepSnapshotOSDiskShouldNotExecute(t *testing.T) { func TestStepSnapshotOSDiskShouldPassIfSnapshotPasses(t *testing.T) { var testSubject = &StepSnapshotOSDisk{ - create: func(context.Context, string, string, string, map[string]*string, string) error { + create: func(context.Context, string, string, string, string, map[string]string, string) error { return nil }, say: func(message string) {}, @@ -83,10 +83,15 @@ func createTestStateBagStepSnapshotOSDisk() multistep.StateBag { "tag01": &value, } + newSDKTags := map[string]string{ + "tag02:": value, + } stateBag.Put(constants.ArmTags, tags) + stateBag.Put(constants.ArmNewSDKTags, newSDKTags) stateBag.Put(constants.ArmOSDiskVhd, "subscriptions/123-456-789/resourceGroups/existingresourcegroup/providers/Microsoft.Compute/disks/osdisk") stateBag.Put(constants.ArmManagedImageOSDiskSnapshotName, "Unit Test: ManagedImageOSDiskSnapshotName") + stateBag.Put(constants.ArmSubscription, "Unit Test: Subscription") return stateBag } diff --git a/builder/azure/arm/step_validate_template.go b/builder/azure/arm/step_validate_template.go index 1d7d4418..6b69eceb 100644 --- a/builder/azure/arm/step_validate_template.go +++ b/builder/azure/arm/step_validate_template.go @@ -7,6 +7,7 @@ import ( "context" "fmt" + hashiDeploymentsSDK "github.com/hashicorp/go-azure-sdk/resource-manager/resources/2022-09-01/deployments" "github.com/hashicorp/packer-plugin-azure/builder/azure/common/constants" "github.com/hashicorp/packer-plugin-sdk/multistep" packersdk "github.com/hashicorp/packer-plugin-sdk/packer" @@ -14,7 +15,7 @@ import ( type StepValidateTemplate struct { client *AzureClient - validate func(ctx context.Context, resourceGroupName string, deploymentName string) error + validate func(ctx context.Context, subscriptionId string, resourceGroupName string, deploymentName string) error say func(message string) error func(e error) config *Config @@ -36,13 +37,13 @@ func NewStepValidateTemplate(client *AzureClient, ui packersdk.Ui, config *Confi return step } -func (s *StepValidateTemplate) validateTemplate(ctx context.Context, resourceGroupName string, deploymentName string) error { +func (s *StepValidateTemplate) validateTemplate(ctx context.Context, subscriptionId string, 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) + id := hashiDeploymentsSDK.NewResourceGroupProviderDeploymentID(subscriptionId, resourceGroupName, deploymentName) + _, err = s.client.DeploymentsClient.Validate(ctx, id, *deployment) if err != nil { s.say(s.client.LastError.Error()) } @@ -53,11 +54,12 @@ func (s *StepValidateTemplate) Run(ctx context.Context, state multistep.StateBag s.say("Validating deployment template ...") var resourceGroupName = state.Get(constants.ArmResourceGroupName).(string) + var subscriptionId = state.Get(constants.ArmSubscription).(string) s.say(fmt.Sprintf(" -> ResourceGroupName : '%s'", resourceGroupName)) s.say(fmt.Sprintf(" -> DeploymentName : '%s'", s.name)) - err := s.validate(ctx, resourceGroupName, s.name) + err := s.validate(ctx, subscriptionId, resourceGroupName, s.name) return processStepResult(err, s.error, state) } diff --git a/builder/azure/arm/step_validate_template_test.go b/builder/azure/arm/step_validate_template_test.go index dd967143..6f9e53f3 100644 --- a/builder/azure/arm/step_validate_template_test.go +++ b/builder/azure/arm/step_validate_template_test.go @@ -14,7 +14,7 @@ import ( func TestStepValidateTemplateShouldFailIfValidateFails(t *testing.T) { var testSubject = &StepValidateTemplate{ - validate: func(context.Context, string, string) error { return fmt.Errorf("!! Unit Test FAIL !!") }, + validate: func(context.Context, string, string, string) error { return fmt.Errorf("!! Unit Test FAIL !!") }, say: func(message string) {}, error: func(e error) {}, } @@ -33,7 +33,7 @@ func TestStepValidateTemplateShouldFailIfValidateFails(t *testing.T) { func TestStepValidateTemplateShouldPassIfValidatePasses(t *testing.T) { var testSubject = &StepValidateTemplate{ - validate: func(context.Context, string, string) error { return nil }, + validate: func(context.Context, string, string, string) error { return nil }, say: func(message string) {}, error: func(e error) {}, } @@ -52,11 +52,12 @@ func TestStepValidateTemplateShouldPassIfValidatePasses(t *testing.T) { func TestStepValidateTemplateShouldTakeResourceGroupNameArgumentFromStateBag(t *testing.T) { var actualResourceGroupName string + var actualSubscriptionId string var testSubject = &StepValidateTemplate{ - validate: func(ctx context.Context, resourceGroupName string, deploymentName string) error { + validate: func(ctx context.Context, subscriptionId string, resourceGroupName string, deploymentName string) error { actualResourceGroupName = resourceGroupName - + actualSubscriptionId = subscriptionId return nil }, say: func(message string) {}, @@ -71,10 +72,15 @@ func TestStepValidateTemplateShouldTakeResourceGroupNameArgumentFromStateBag(t * } var expectedResourceGroupName = stateBag.Get(constants.ArmResourceGroupName).(string) + var expectedSubscriptionId = stateBag.Get(constants.ArmSubscription).(string) if actualResourceGroupName != expectedResourceGroupName { t.Fatal("Expected the step to source 'constants.ArmResourceGroupName' from the state bag, but it did not.") } + + if actualSubscriptionId != expectedSubscriptionId { + t.Fatal("Expected the step to source 'constants.ArmSubscription' from the state bag, but it did not.") + } } func TestStepValidateTemplateShouldTakeDeploymentNameArgumentFromParam(t *testing.T) { @@ -83,7 +89,7 @@ func TestStepValidateTemplateShouldTakeDeploymentNameArgumentFromParam(t *testin stateBag := createTestStateBagStepValidateTemplate() var testSubject = &StepValidateTemplate{ - validate: func(ctx context.Context, resourceGroupName string, deploymentName string) error { + validate: func(ctx context.Context, subscriptionId string, resourceGroupName string, deploymentName string) error { actualDeploymentName = deploymentName return nil @@ -109,6 +115,7 @@ func createTestStateBagStepValidateTemplate() multistep.StateBag { stateBag.Put(constants.ArmDeploymentName, "Unit Test: DeploymentName") stateBag.Put(constants.ArmResourceGroupName, "Unit Test: ResourceGroupName") + stateBag.Put(constants.ArmSubscription, "Unit Test: Subscription") return stateBag } diff --git a/builder/azure/arm/template_factory.go b/builder/azure/arm/template_factory.go index 83ba64fe..54654c93 100644 --- a/builder/azure/arm/template_factory.go +++ b/builder/azure/arm/template_factory.go @@ -9,17 +9,16 @@ import ( "errors" "fmt" - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2021-11-01/compute" - "github.com/Azure/azure-sdk-for-go/services/resources/mgmt/2018-02-01/resources" - + hashiVMSDK "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-01/virtualmachines" + "github.com/hashicorp/go-azure-sdk/resource-manager/resources/2022-09-01/deployments" "github.com/hashicorp/packer-plugin-azure/builder/azure/common/constants" "github.com/hashicorp/packer-plugin-azure/builder/azure/common/template" "golang.org/x/crypto/ssh" ) -type templateFactoryFunc func(*Config) (*resources.Deployment, error) +type templateFactoryFunc func(*Config) (*deployments.Deployment, error) -func GetCommunicatorSpecificKeyVaultDeployment(config *Config) (*resources.Deployment, error) { +func GetCommunicatorSpecificKeyVaultDeployment(config *Config) (*deployments.Deployment, error) { if config.Comm.Type == "ssh" { privateKey, err := ssh.ParseRawPrivateKey(config.Comm.SSHPrivateKey) if err != nil { @@ -40,7 +39,7 @@ func GetCommunicatorSpecificKeyVaultDeployment(config *Config) (*resources.Deplo } } -func GetKeyVaultDeployment(config *Config, secretValue string) (*resources.Deployment, error) { +func GetKeyVaultDeployment(config *Config, secretValue string) (*deployments.Deployment, error) { params := &template.TemplateParameters{ KeyVaultName: &template.TemplateParameter{Value: config.tmpKeyVaultName}, KeyVaultSKU: &template.TemplateParameter{Value: config.BuildKeyVaultSKU}, @@ -56,7 +55,7 @@ func GetKeyVaultDeployment(config *Config, secretValue string) (*resources.Deplo return createDeploymentParameters(*doc, params) } -func GetSpecializedVirtualMachineDeployment(config *Config) (*resources.Deployment, error) { +func GetSpecializedVirtualMachineDeployment(config *Config) (*deployments.Deployment, error) { builder, err := GetVirtualMachineTemplateBuilder(config) if err != nil { return nil, err @@ -86,7 +85,7 @@ func GetSpecializedVirtualMachineDeployment(config *Config) (*resources.Deployme return createDeploymentParameters(*doc, params) } -func GetVirtualMachineDeployment(config *Config) (*resources.Deployment, error) { +func GetVirtualMachineDeployment(config *Config) (*deployments.Deployment, error) { builder, err := GetVirtualMachineTemplateBuilder(config) if err != nil { return nil, err @@ -117,7 +116,7 @@ func GetVirtualMachineTemplateBuilder(config *Config) (*template.TemplateBuilder if err != nil { return nil, err } - osType := compute.OperatingSystemTypesLinux + osType := hashiVMSDK.OperatingSystemTypesLinux switch config.OSType { case constants.Target_Linux: @@ -129,7 +128,7 @@ func GetVirtualMachineTemplateBuilder(config *Config) (*template.TemplateBuilder return nil, err } case constants.Target_Windows: - osType = compute.OperatingSystemTypesWindows + osType = hashiVMSDK.OperatingSystemTypesWindows err = builder.BuildWindows(config.Comm.Type, config.tmpKeyVaultName, config.tmpWinRMCertificateUrl) if err != nil { return nil, err @@ -286,8 +285,8 @@ func GetVirtualMachineTemplateBuilder(config *Config) (*template.TemplateBuilder return builder, nil } -func createDeploymentParameters(doc string, parameters *template.TemplateParameters) (*resources.Deployment, error) { - var template map[string]interface{} +func createDeploymentParameters(doc string, parameters *template.TemplateParameters) (*deployments.Deployment, error) { + var template interface{} err := json.Unmarshal(([]byte)(doc), &template) if err != nil { return nil, err @@ -298,15 +297,15 @@ func createDeploymentParameters(doc string, parameters *template.TemplateParamet return nil, err } - var templateParameters map[string]interface{} + var templateParameters interface{} err = json.Unmarshal(bs, &templateParameters) if err != nil { return nil, err } - return &resources.Deployment{ - Properties: &resources.DeploymentProperties{ - Mode: resources.Incremental, + return &deployments.Deployment{ + Properties: deployments.DeploymentProperties{ + Mode: deployments.DeploymentModeIncremental, Template: &template, Parameters: &templateParameters, }, diff --git a/builder/azure/arm/template_factory_test.TestVirtualMachineDeployment13.approved.json b/builder/azure/arm/template_factory_test.TestVirtualMachineDeployment13.approved.json index 6df7770f..4cde2f19 100644 --- a/builder/azure/arm/template_factory_test.TestVirtualMachineDeployment13.approved.json +++ b/builder/azure/arm/template_factory_test.TestVirtualMachineDeployment13.approved.json @@ -130,7 +130,7 @@ "listeners": [ { "certificateUrl": "", - "protocol": "https" + "protocol": "Https" } ] } diff --git a/builder/azure/arm/template_factory_test.go b/builder/azure/arm/template_factory_test.go index b56a630b..f6652a59 100644 --- a/builder/azure/arm/template_factory_test.go +++ b/builder/azure/arm/template_factory_test.go @@ -8,8 +8,8 @@ import ( "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/go-azure-sdk/resource-manager/resources/2022-09-01/deployments" "github.com/hashicorp/packer-plugin-azure/builder/azure/common/constants" "github.com/hashicorp/packer-plugin-azure/builder/azure/common/template" ) @@ -26,8 +26,8 @@ func TestVirtualMachineDeployment00(t *testing.T) { 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.Mode != deployments.DeploymentModeIncremental { + t.Errorf("Expected deployment.Properties.Mode to be %s, but got %s", deployments.DeploymentModeIncremental, deployment.Properties.Mode) } if deployment.Properties.ParametersLink != nil { @@ -533,8 +533,8 @@ func TestKeyVaultDeployment00(t *testing.T) { 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.Mode != deployments.DeploymentModeIncremental { + t.Errorf("Expected deployment.Properties.Mode to be %s, but got %s", deployments.DeploymentModeIncremental, deployment.Properties.Mode) } if deployment.Properties.ParametersLink != nil { diff --git a/builder/azure/common/client/config.go b/builder/azure/common/client/config.go index 659aa4e6..30ad722f 100644 --- a/builder/azure/common/client/config.go +++ b/builder/azure/common/client/config.go @@ -17,6 +17,7 @@ import ( "github.com/Azure/go-autorest/autorest/azure" jwt "github.com/golang-jwt/jwt" "github.com/hashicorp/go-azure-helpers/authentication" + "github.com/hashicorp/go-azure-sdk/sdk/environments" packersdk "github.com/hashicorp/packer-plugin-sdk/packer" ) @@ -36,6 +37,7 @@ type Config struct { // USGovernmentCloud and AzureUSGovernmentCloud are also supported. CloudEnvironmentName string `mapstructure:"cloud_environment_name" required:"false"` cloudEnvironment *azure.Environment + newCloudEnvironment *environments.Environment // The Hostname of the Azure Metadata Service // (for example management.azure.com), used to obtain the Cloud Environment // when using a Custom Azure Environment. This can also be sourced from the @@ -85,12 +87,12 @@ type Config struct { } const ( - authTypeDeviceLogin = "DeviceLogin" - authTypeMSI = "ManagedIdentity" - authTypeClientSecret = "ClientSecret" - authTypeClientCert = "ClientCertificate" - authTypeClientBearerJWT = "ClientBearerJWT" - authTypeAzureCLI = "AzureCLI" + AuthTypeDeviceLogin = "DeviceLogin" + AuthTypeMSI = "ManagedIdentity" + AuthTypeClientSecret = "ClientSecret" + AuthTypeClientCert = "ClientCertificate" + AuthTypeClientBearerJWT = "ClientBearerJWT" + AuthTypeAzureCLI = "AzureCLI" ) const DefaultCloudEnvironmentName = "Public" @@ -102,12 +104,73 @@ func (c *Config) SetDefaultValues() error { c.CloudEnvironmentName = DefaultCloudEnvironmentName } + err := c.setNewCloudEnvironment() + if err != nil { + return err + } return c.setCloudEnvironment() } func (c *Config) CloudEnvironment() *azure.Environment { return c.cloudEnvironment } +func (c *Config) NewCloudEnvironment() *environments.Environment { + return c.newCloudEnvironment +} +func (c *Config) AuthType() string { + return c.authType +} + +func (c *Config) setNewCloudEnvironment() error { + if c.MetadataHost == "" { + if v := os.Getenv("ARM_METADATA_URL"); v != "" { + c.MetadataHost = v + } + } + env, err := environments.FromEndpoint(context.TODO(), c.MetadataHost, c.CloudEnvironmentName) + c.newCloudEnvironment = env + if err != nil { + // fall back to old method of normalizing and looking up cloud names. + log.Printf(fmt.Sprintf("Error looking up environment using metadata host: %s. \n"+ + "Falling back to hardcoded mechanism...", err.Error())) + lookup := map[string]string{ + "CHINA": "china", + "CHINACLOUD": "china", + "AZURECHINACLOUD": "china", + + // TODO Implement AzureGermanCloud + // I reached out to the provider team and will try to get it implemented in the SDK first` + "GERMAN": "AzureGermanCloud", + "GERMANCLOUD": "AzureGermanCloud", + "AZUREGERMANCLOUD": "AzureGermanCloud", + + "GERMANY": "AzureGermanCloud", + "GERMANYCLOUD": "AzureGermanCloud", + "AZUREGERMANYCLOUD": "AzureGermanCloud", + + "PUBLIC": "public", + "PUBLICCLOUD": "public", + "AZUREPUBLICCLOUD": "public", + + "USGOVERNMENT": "usgovernment", + "USGOVERNMENTCLOUD": "usgovernment", + "AZUREUSGOVERNMENTCLOUD": "usgovernment", + } + + 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 := environments.FromName(envName) + if err != nil { + return err + } + c.newCloudEnvironment = env + } + return nil +} func (c *Config) setCloudEnvironment() error { // First, try using the metadata host to look up the cloud. @@ -224,9 +287,9 @@ func (c Config) Validate(errs *packersdk.MultiError) { 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 claims.ExpiresAt < time.Now().Add(5*time.Minute).Unix() { + // errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("%d %d, client_jwt will expire within 5 minutes, please use a JWT that is valid for at least 5 minutes", claims.ExpiresAt, time.Now().Add(5*time.Minute).Unix())) + //} 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")) } @@ -293,29 +356,29 @@ func (c Config) GetServicePrincipalToken( var auth oAuthTokenProvider switch c.authType { - case authTypeDeviceLogin: + case AuthTypeDeviceLogin: say("Getting tokens using device flow") auth = NewDeviceFlowOAuthTokenProvider(*c.cloudEnvironment, say, c.TenantID) - case authTypeAzureCLI: + case AuthTypeAzureCLI: say("Getting tokens using Azure CLI") auth = NewCliOAuthTokenProvider(*c.cloudEnvironment, say, c.TenantID) - case authTypeMSI: + case AuthTypeMSI: say("Getting tokens using Managed Identity for Azure") auth = NewMSIOAuthTokenProvider(*c.cloudEnvironment, c.ClientID) - case authTypeClientSecret: + case AuthTypeClientSecret: say("Getting tokens using client secret") auth = NewSecretOAuthTokenProvider(*c.cloudEnvironment, c.ClientID, c.ClientSecret, c.TenantID) - case authTypeClientCert: + 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: + 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") + panic("AuthType not set, call FillParameters, or set explicitly") } servicePrincipalToken, err = auth.getServicePrincipalTokenWithResource(forResource) @@ -331,26 +394,26 @@ func (c Config) GetServicePrincipalToken( return servicePrincipalToken, nil } -// FillParameters capture the user intent from the supplied parameter set in authType, retrieves the TenantID and CloudEnvironment if not specified. +// 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 + c.authType = AuthTypeDeviceLogin } else if c.UseCLI() { - c.authType = authTypeAzureCLI + c.authType = AuthTypeAzureCLI } else if c.UseMSI() { - c.authType = authTypeMSI + c.authType = AuthTypeMSI } else if c.ClientSecret != "" { - c.authType = authTypeClientSecret + c.authType = AuthTypeClientSecret } else if c.ClientCertPath != "" { - c.authType = authTypeClientCert + c.authType = AuthTypeClientCert } else { - c.authType = authTypeClientBearerJWT + c.authType = AuthTypeClientBearerJWT } } - if c.authType == authTypeMSI && c.SubscriptionID == "" { + if c.authType == AuthTypeMSI && c.SubscriptionID == "" { subscriptionID, err := getSubscriptionFromIMDS() if err != nil { @@ -364,9 +427,16 @@ func (c *Config) FillParameters() error { if err != nil { return err } + } - if c.authType == authTypeAzureCLI { + if c.newCloudEnvironment == nil { + newCloudErr := c.setNewCloudEnvironment() + if newCloudErr != nil { + return newCloudErr + } + } + 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) diff --git a/builder/azure/common/client/config_test.go b/builder/azure/common/client/config_test.go index d3678e80..3d9affc7 100644 --- a/builder/azure/common/client/config_test.go +++ b/builder/azure/common/client/config_test.go @@ -17,6 +17,7 @@ import ( "github.com/Azure/go-autorest/autorest/azure" jwt "github.com/golang-jwt/jwt" + "github.com/hashicorp/go-azure-sdk/sdk/environments" packersdk "github.com/hashicorp/packer-plugin-sdk/packer" ) @@ -190,8 +191,9 @@ func Test_ClientConfig_AzureCli(t *testing.T) { getEnvOrSkip(t, "AZURE_CLI_AUTH") cfg := Config{ - UseAzureCLIAuth: true, - cloudEnvironment: getCloud(), + UseAzureCLIAuth: true, + cloudEnvironment: getCloud(), + newCloudEnvironment: environments.AzurePublic(), } assertValid(t, cfg) @@ -200,8 +202,8 @@ func Test_ClientConfig_AzureCli(t *testing.T) { 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) + if cfg.AuthType() != AuthTypeAzureCLI { + t.Fatalf("Expected authType to be %q, but got: %q", AuthTypeAzureCLI, cfg.AuthType()) } } @@ -402,16 +404,6 @@ func Test_ClientConfig_CannotUseBothClientJWTAndSecret(t *testing.T) { 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", diff --git a/builder/azure/common/client/tokenprovider_cli.go b/builder/azure/common/client/tokenprovider_cli.go index 82766cac..603d872d 100644 --- a/builder/azure/common/client/tokenprovider_cli.go +++ b/builder/azure/common/client/tokenprovider_cli.go @@ -4,13 +4,20 @@ package client import ( + "bytes" "context" + "encoding/json" "errors" "fmt" + "io/ioutil" + "os" + "path/filepath" "github.com/Azure/go-autorest/autorest/adal" "github.com/Azure/go-autorest/autorest/azure" "github.com/Azure/go-autorest/autorest/azure/cli" + "github.com/dimchansky/utfbom" + "github.com/mitchellh/go-homedir" ) // for managed identity auth @@ -100,3 +107,59 @@ func getIDsFromAzureCLI() (string, string, error) { return "", "", errors.New("Unable to find default subscription") } + +const azureProfileJSON = "azureProfile.json" + +func configDir() string { + return os.Getenv("AZURE_CONFIG_DIR") +} + +// ProfilePath returns the path where the Azure Profile is stored from the Azure CLI +func ProfilePath() (string, error) { + if cfgDir := configDir(); cfgDir != "" { + return filepath.Join(cfgDir, azureProfileJSON), nil + } + return homedir.Expand("~/.azure/" + azureProfileJSON) +} + +// Profile represents a Profile from the Azure CLI +type Profile struct { + InstallationID string `json:"installationId"` + Subscriptions []Subscription `json:"subscriptions"` +} + +// Subscription represents a Subscription from the Azure CLI +type Subscription struct { + EnvironmentName string `json:"environmentName"` + ID string `json:"id"` + IsDefault bool `json:"isDefault"` + Name string `json:"name"` + State string `json:"state"` + TenantID string `json:"tenantId"` + User *User `json:"user"` +} + +// User represents a User from the Azure CLI +type User struct { + Name string `json:"name"` + Type string `json:"type"` +} + +// LoadProfile restores a Profile object from a file located at 'path'. +func LoadProfile(path string) (result Profile, err error) { + var contents []byte + contents, err = ioutil.ReadFile(path) + if err != nil { + err = fmt.Errorf("failed to open file (%s) while loading token: %v", path, err) + return + } + reader := utfbom.SkipOnly(bytes.NewReader(contents)) + + dec := json.NewDecoder(reader) + if err = dec.Decode(&result); err != nil { + err = fmt.Errorf("failed to decode contents of file (%s) into a Profile representation: %v", path, err) + return + } + + return +} diff --git a/builder/azure/common/constants/stateBag.go b/builder/azure/common/constants/stateBag.go index 63ee6b54..42450469 100644 --- a/builder/azure/common/constants/stateBag.go +++ b/builder/azure/common/constants/stateBag.go @@ -15,28 +15,34 @@ const ( // Default replica count for image versions in shared image gallery const ( - SharedImageGalleryImageVersionDefaultMinReplicaCount int32 = 1 - SharedImageGalleryImageVersionDefaultMaxReplicaCount int32 = 100 + SharedImageGalleryImageVersionDefaultMinReplicaCount int64 = 1 + SharedImageGalleryImageVersionDefaultMaxReplicaCount int64 = 100 ) 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" + 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" + // TODO Replace ArmTags with ArmNewSDKTags + // Temporary object, new SDK expects *map[string]string instead of map [string]*string + ArmNewSDKTags string = "arm.NewSDKTags" + ArmStorageAccountName string = "arm.StorageAccountName" + ArmTags string = "arm.Tags" + // TODO Replace ArmVirtualMachineCaptureParameters with ArmNewVirtualMachineCaptureParameters + // Temporary object, this code is shared by all three builders so we need a new object for the diff type of capture parameters + ArmNewVirtualMachineCaptureParameters string = "arm.NewVirtualMachineCaptureParameters" ArmVirtualMachineCaptureParameters string = "arm.VirtualMachineCaptureParameters" ArmIsExistingResourceGroup string = "arm.IsExistingResourceGroup" ArmIsExistingKeyVault string = "arm.IsExistingKeyVault" @@ -54,6 +60,7 @@ const ( ArmManagedImageSharedGalleryImageVersionReplicaCount string = "arm.ArmManagedImageSharedGalleryImageVersionReplicaCount" ArmManagedImageSharedGalleryImageVersionExcludeFromLatest string = "arm.ArmManagedImageSharedGalleryImageVersionExcludeFromLatest" ArmManagedImageSharedGalleryImageVersionStorageAccountType string = "arm.ArmManagedImageSharedGalleryImageVersionStorageAccountType" + ArmSharedImageGalleryDestinationSubscription string = "arm.ArmSharedImageGalleryDestinationSubscription" ArmSharedImageGalleryDestinationSpecialized string = "arm.ArmSharedImageGalleryDestinationSpecialized" ArmManagedImageSubscription string = "arm.ArmManagedImageSubscription" ArmAsyncResourceGroupDelete string = "arm.AsyncResourceGroupDelete" @@ -61,4 +68,5 @@ const ( ArmManagedImageDataDiskSnapshotPrefix string = "arm.ManagedImageDataDiskSnapshotPrefix" ArmKeepOSDisk string = "arm.KeepOSDisk" ArmBuildDiskEncryptionSetId string = "arm.ArmBuildDiskEncryptionSetId" + ArmSubscription string = "arm.Subscription" ) diff --git a/builder/azure/common/pointer.go b/builder/azure/common/pointer.go new file mode 100644 index 00000000..c58b7cd6 --- /dev/null +++ b/builder/azure/common/pointer.go @@ -0,0 +1,36 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +//go:generate packer-sdc struct-markdown + +package common + +// StringPtr returns a pointer to the passed string. +func StringPtr(s string) *string { + return &s +} + +// BoolPtr returns a pointer to the passed bool. +func BoolPtr(b bool) *bool { + return &b +} + +// IntPtr returns a pointer to the passed int. +func IntPtr(i int) *int { + return &i +} + +// Int32Ptr returns a pointer to the passed int32. +func Int32Ptr(i int32) *int32 { + return &i +} + +// Int64Ptr returns a pointer to the passed int64. +func Int64Ptr(i int64) *int64 { + return &i +} + +// Float64Ptr returns a pointer to the passed float64. +func Float64Ptr(i float64) *float64 { + return &i +} diff --git a/builder/azure/common/template/template.go b/builder/azure/common/template/template.go index c088ab3e..02cd42b5 100644 --- a/builder/azure/common/template/template.go +++ b/builder/azure/common/template/template.go @@ -4,8 +4,14 @@ package template import ( - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2021-11-01/compute" - "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2018-01-01/network" + hashiImagesSDK "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-01/images" + hashiVMSDK "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-01/virtualmachines" + + hashiSecurityRulesSDK "github.com/hashicorp/go-azure-sdk/resource-manager/network/2022-09-01/securityrules" + + hashiPublicIPSDK "github.com/hashicorp/go-azure-sdk/resource-manager/network/2022-09-01/publicipaddresses" + hashiSubnetsSDK "github.com/hashicorp/go-azure-sdk/resource-manager/network/2022-09-01/subnets" + hashiVNETSDK "github.com/hashicorp/go-azure-sdk/resource-manager/network/2022-09-01/virtualnetworks" ) // Template @@ -46,43 +52,48 @@ type Plan struct { } type ManagedDisk struct { - StorageAccountType compute.StorageAccountTypes `json:"storageAccountType,omitempty"` - DiskEncryptionSet *compute.DiskEncryptionSetParameters `json:"diskEncryptionSet,omitempty"` - SecurityProfile *compute.VMDiskSecurityProfile `json:"securityProfile,omitempty"` + StorageAccountType hashiVMSDK.StorageAccountTypes `json:"storageAccountType,omitempty"` + DiskEncryptionSet *DiskEncryptionSetParameters `json:"diskEncryptionSet,omitempty"` + SecurityProfile *hashiVMSDK.VMDiskSecurityProfile `json:"securityProfile,omitempty"` + // ID - Resource Id + ID *string `json:"id,omitempty"` +} + +type DiskEncryptionSetParameters struct { // ID - Resource Id ID *string `json:"id,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 *ManagedDisk `json:"managedDisk,omitempty"` + OsType hashiVMSDK.OperatingSystemTypes `json:"osType,omitempty"` + OsState hashiImagesSDK.OperatingSystemStateTypes `json:"osState,omitempty"` + BlobURI *string `json:"blobUri,omitempty"` + Name *string `json:"name,omitempty"` + Vhd *hashiVMSDK.VirtualHardDisk `json:"vhd,omitempty"` + Image *hashiVMSDK.VirtualHardDisk `json:"image,omitempty"` + Caching hashiVMSDK.CachingTypes `json:"caching,omitempty"` + CreateOption hashiVMSDK.DiskCreateOptionTypes `json:"createOption,omitempty"` + DiskSizeGB *int32 `json:"diskSizeGB,omitempty"` + ManagedDisk *ManagedDisk `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 *ManagedDisk `json:"managedDisk,omitempty"` + Lun *int `json:"lun,omitempty"` + BlobURI *string `json:"blobUri,omitempty"` + Name *string `json:"name,omitempty"` + Vhd *hashiVMSDK.VirtualHardDisk `json:"vhd,omitempty"` + Image *hashiVMSDK.VirtualHardDisk `json:"image,omitempty"` + Caching hashiVMSDK.CachingTypes `json:"caching,omitempty"` + CreateOption hashiVMSDK.DiskCreateOptionTypes `json:"createOption,omitempty"` + DiskSizeGB *int32 `json:"diskSizeGB,omitempty"` + ManagedDisk *ManagedDisk `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"` + ImageReference *hashiVMSDK.ImageReference `json:"imageReference,omitempty"` + OsDisk *OSDiskUnion `json:"osDisk,omitempty"` + DataDisks *[]DataDiskUnion `json:"dataDisks,omitempty"` } type BillingProfile struct { @@ -91,30 +102,30 @@ type BillingProfile struct { // 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"` - LicenseType *string `json:"licenseType,omitempty"` - NetworkProfile *compute.NetworkProfile `json:"networkProfile,omitempty"` - OsProfile *compute.OSProfile `json:"osProfile,omitempty"` - PublicIPAllocatedMethod *network.IPAllocationMethod `json:"publicIPAllocationMethod,omitempty"` - Sku *Sku `json:"sku,omitempty"` - UserData *string `json:"userData,omitempty"` - StorageProfile *StorageProfileUnion `json:"storageProfile,omitempty"` - SecurityProfile *compute.SecurityProfile `json:"securityProfile,omitempty"` - Subnets *[]network.Subnet `json:"subnets,omitempty"` - SecurityRules *[]network.SecurityRule `json:"securityRules,omitempty"` - TenantId *string `json:"tenantId,omitempty"` - Value *string `json:"value,omitempty"` - Priority *string `json:"priority,omitempty"` - EvictionPolicy *compute.VirtualMachineEvictionPolicyTypes `json:"evictionPolicy,omitempty"` - BillingProfile *BillingProfile `json:"billingProfile,omitempty"` + AccessPolicies *[]AccessPolicies `json:"accessPolicies,omitempty"` + AddressSpace *hashiVNETSDK.AddressSpace `json:"addressSpace,omitempty"` + DiagnosticsProfile *hashiVMSDK.DiagnosticsProfile `json:"diagnosticsProfile,omitempty"` + DNSSettings *hashiPublicIPSDK.PublicIPAddressDnsSettings `json:"dnsSettings,omitempty"` + EnabledForDeployment *string `json:"enabledForDeployment,omitempty"` + EnabledForTemplateDeployment *string `json:"enabledForTemplateDeployment,omitempty"` + EnableSoftDelete *string `json:"enableSoftDelete,omitempty"` + HardwareProfile *hashiVMSDK.HardwareProfile `json:"hardwareProfile,omitempty"` + IPConfigurations *[]hashiPublicIPSDK.IPConfiguration `json:"ipConfigurations,omitempty"` + LicenseType *string `json:"licenseType,omitempty"` + NetworkProfile *hashiVMSDK.NetworkProfile `json:"networkProfile,omitempty"` + OsProfile *hashiVMSDK.OSProfile `json:"osProfile,omitempty"` + PublicIPAllocatedMethod *hashiPublicIPSDK.IPAllocationMethod `json:"publicIPAllocationMethod,omitempty"` + Sku *Sku `json:"sku,omitempty"` + UserData *string `json:"userData,omitempty"` + StorageProfile *StorageProfileUnion `json:"storageProfile,omitempty"` + SecurityProfile *hashiVMSDK.SecurityProfile `json:"securityProfile,omitempty"` + Subnets *[]hashiSubnetsSDK.Subnet `json:"subnets,omitempty"` + SecurityRules *[]hashiSecurityRulesSDK.SecurityRule `json:"securityRules,omitempty"` + TenantId *string `json:"tenantId,omitempty"` + Value *string `json:"value,omitempty"` + Priority *string `json:"priority,omitempty"` + EvictionPolicy *hashiVMSDK.VirtualMachineEvictionPolicyTypes `json:"evictionPolicy,omitempty"` + BillingProfile *BillingProfile `json:"billingProfile,omitempty"` //CustomScript extension related properties Publisher *string `json:"publisher,omitempty"` Type *string `json:"type,omitempty"` diff --git a/builder/azure/common/template/template_builder.go b/builder/azure/common/template/template_builder.go index 308b628a..d68528e2 100644 --- a/builder/azure/common/template/template_builder.go +++ b/builder/azure/common/template/template_builder.go @@ -9,9 +9,10 @@ import ( "strconv" "strings" - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2021-11-01/compute" - "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2018-01-01/network" - "github.com/Azure/go-autorest/autorest/to" + hashiVMSDK "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-01/virtualmachines" + hashiSecurityRulesSDK "github.com/hashicorp/go-azure-sdk/resource-manager/network/2022-09-01/securityrules" + hashiSubnetsSDK "github.com/hashicorp/go-azure-sdk/resource-manager/network/2022-09-01/subnets" + "github.com/hashicorp/packer-plugin-azure/builder/azure/common" ) const ( @@ -32,7 +33,7 @@ const ( type TemplateBuilder struct { template *Template - osType compute.OperatingSystemTypes + osType hashiVMSDK.OperatingSystemTypes } func NewTemplateBuilder(template string) (*TemplateBuilder, error) { @@ -54,24 +55,25 @@ func (s *TemplateBuilder) BuildLinux(sshAuthorizedKey string, disablePasswordAut return err } + variableSshKeyPath := s.toVariable(variableSshKeyPath) profile := resource.Properties.OsProfile - profile.LinuxConfiguration = &compute.LinuxConfiguration{ - SSH: &compute.SSHConfiguration{ - PublicKeys: &[]compute.SSHPublicKey{ + profile.LinuxConfiguration = &hashiVMSDK.LinuxConfiguration{ + Ssh: &hashiVMSDK.SshConfiguration{ + PublicKeys: &[]hashiVMSDK.SshPublicKey{ { - Path: to.StringPtr(s.toVariable(variableSshKeyPath)), - KeyData: to.StringPtr(sshAuthorizedKey), + Path: &variableSshKeyPath, + KeyData: &sshAuthorizedKey, }, }, }, } if disablePasswordAuthentication { - profile.LinuxConfiguration.DisablePasswordAuthentication = to.BoolPtr(true) + profile.LinuxConfiguration.DisablePasswordAuthentication = common.BoolPtr(true) profile.AdminPassword = nil } - s.osType = compute.OperatingSystemTypesLinux + s.osType = hashiVMSDK.OperatingSystemTypesLinux return nil } @@ -82,36 +84,40 @@ func (s *TemplateBuilder) BuildWindows(communicatorType string, keyVaultName str } profile := resource.Properties.OsProfile - s.osType = compute.OperatingSystemTypesWindows + s.osType = hashiVMSDK.OperatingSystemTypesWindows - profile.Secrets = &[]compute.VaultSecretGroup{ + certifacteStore := "My" + resourceID := s.toResourceID(resourceKeyVaults, keyVaultName) + profile.Secrets = &[]hashiVMSDK.VaultSecretGroup{ { - SourceVault: &compute.SubResource{ - ID: to.StringPtr(s.toResourceID(resourceKeyVaults, keyVaultName)), + SourceVault: &hashiVMSDK.SubResource{ + Id: &resourceID, }, - VaultCertificates: &[]compute.VaultCertificate{ + VaultCertificates: &[]hashiVMSDK.VaultCertificate{ { - CertificateStore: to.StringPtr("My"), - CertificateURL: to.StringPtr(certificateUrl), + CertificateStore: &certifacteStore, + CertificateUrl: &certificateUrl, }, }, }, } + provisionVMAgent := true if communicatorType == "ssh" { - profile.WindowsConfiguration = &compute.WindowsConfiguration{ - ProvisionVMAgent: to.BoolPtr(true), + profile.WindowsConfiguration = &hashiVMSDK.WindowsConfiguration{ + ProvisionVMAgent: &provisionVMAgent, } return nil } - profile.WindowsConfiguration = &compute.WindowsConfiguration{ - ProvisionVMAgent: to.BoolPtr(true), - WinRM: &compute.WinRMConfiguration{ - Listeners: &[]compute.WinRMListener{ + protocol := hashiVMSDK.ProtocolTypesHTTPS + profile.WindowsConfiguration = &hashiVMSDK.WindowsConfiguration{ + ProvisionVMAgent: common.BoolPtr(true), + WinRM: &hashiVMSDK.WinRMConfiguration{ + Listeners: &[]hashiVMSDK.WinRMListener{ { - Protocol: "https", - CertificateURL: to.StringPtr(certificateUrl), + Protocol: &protocol, + CertificateUrl: common.StringPtr(certificateUrl), }, }, }, @@ -131,7 +137,7 @@ func (s *TemplateBuilder) SetIdentity(userAssignedManagedIdentities []string) er if len(userAssignedManagedIdentities) != 0 { s.setVariable("apiVersion", "2018-06-01") // Required for user assigned managed identity id = &Identity{ - Type: to.StringPtr("UserAssigned"), + Type: common.StringPtr("UserAssigned"), UserAssignedIdentities: make(map[string]struct{}), } for _, uid := range userAssignedManagedIdentities { @@ -143,18 +149,18 @@ func (s *TemplateBuilder) SetIdentity(userAssignedManagedIdentities []string) er return nil } -func (s *TemplateBuilder) SetManagedDiskUrl(managedImageId string, storageAccountType compute.StorageAccountTypes, cachingType compute.CachingTypes) error { +func (s *TemplateBuilder) SetManagedDiskUrl(managedImageId string, storageAccountType hashiVMSDK.StorageAccountTypes, cachingType hashiVMSDK.CachingTypes) error { resource, err := s.getResourceByType(resourceVirtualMachine) if err != nil { return err } profile := resource.Properties.StorageProfile - profile.ImageReference = &compute.ImageReference{ - ID: &managedImageId, + profile.ImageReference = &hashiVMSDK.ImageReference{ + Id: &managedImageId, } profile.OsDisk.OsType = s.osType - profile.OsDisk.CreateOption = compute.DiskCreateOptionTypesFromImage + profile.OsDisk.CreateOption = hashiVMSDK.DiskCreateOptionTypesFromImage profile.OsDisk.Vhd = nil profile.OsDisk.Caching = cachingType profile.OsDisk.ManagedDisk = &ManagedDisk{ @@ -164,21 +170,21 @@ func (s *TemplateBuilder) SetManagedDiskUrl(managedImageId string, storageAccoun return nil } -func (s *TemplateBuilder) SetManagedMarketplaceImage(publisher, offer, sku, version string, storageAccountType compute.StorageAccountTypes, cachingType compute.CachingTypes) error { +func (s *TemplateBuilder) SetManagedMarketplaceImage(publisher, offer, sku, version string, storageAccountType hashiVMSDK.StorageAccountTypes, cachingType hashiVMSDK.CachingTypes) error { resource, err := s.getResourceByType(resourceVirtualMachine) if err != nil { return err } profile := resource.Properties.StorageProfile - profile.ImageReference = &compute.ImageReference{ + profile.ImageReference = &hashiVMSDK.ImageReference{ Publisher: &publisher, Offer: &offer, Sku: &sku, Version: &version, } profile.OsDisk.OsType = s.osType - profile.OsDisk.CreateOption = compute.DiskCreateOptionTypesFromImage + profile.OsDisk.CreateOption = hashiVMSDK.DiskCreateOptionTypesFromImage profile.OsDisk.Vhd = nil profile.OsDisk.Caching = cachingType profile.OsDisk.ManagedDisk = &ManagedDisk{ @@ -188,7 +194,7 @@ func (s *TemplateBuilder) SetManagedMarketplaceImage(publisher, offer, sku, vers return nil } -func (s *TemplateBuilder) SetSharedGalleryImage(location, imageID string, cachingType compute.CachingTypes) error { +func (s *TemplateBuilder) SetSharedGalleryImage(location, imageID string, cachingType hashiVMSDK.CachingTypes) error { resource, err := s.getResourceByType(resourceVirtualMachine) if err != nil { return err @@ -196,7 +202,7 @@ func (s *TemplateBuilder) SetSharedGalleryImage(location, imageID string, cachin s.setVariable("apiVersion", "2018-06-01") // Required for Shared Image Gallery profile := resource.Properties.StorageProfile - profile.ImageReference = &compute.ImageReference{ID: &imageID} + profile.ImageReference = &hashiVMSDK.ImageReference{Id: &imageID} profile.OsDisk.OsType = s.osType profile.OsDisk.Vhd = nil profile.OsDisk.Caching = cachingType @@ -204,7 +210,7 @@ func (s *TemplateBuilder) SetSharedGalleryImage(location, imageID string, cachin return nil } -func (s *TemplateBuilder) SetCommunityGalleryImage(location, imageID string, cachingType compute.CachingTypes) error { +func (s *TemplateBuilder) SetCommunityGalleryImage(location, imageID string, cachingType hashiVMSDK.CachingTypes) error { resource, err := s.getResourceByType(resourceVirtualMachine) if err != nil { return err @@ -212,7 +218,7 @@ func (s *TemplateBuilder) SetCommunityGalleryImage(location, imageID string, cac s.setVariable("apiVersion", communityGalleryApiVersion) // Required for Community Gallery Image profile := resource.Properties.StorageProfile - profile.ImageReference = &compute.ImageReference{CommunityGalleryImageID: &imageID} + profile.ImageReference = &hashiVMSDK.ImageReference{CommunityGalleryImageId: &imageID} profile.OsDisk.OsType = s.osType profile.OsDisk.Vhd = nil profile.OsDisk.Caching = cachingType @@ -220,7 +226,7 @@ func (s *TemplateBuilder) SetCommunityGalleryImage(location, imageID string, cac return nil } -func (s *TemplateBuilder) SetDirectSharedGalleryImage(location, imageID string, cachingType compute.CachingTypes) error { +func (s *TemplateBuilder) SetDirectSharedGalleryImage(location, imageID string, cachingType hashiVMSDK.CachingTypes) error { resource, err := s.getResourceByType(resourceVirtualMachine) if err != nil { return err @@ -228,7 +234,7 @@ func (s *TemplateBuilder) SetDirectSharedGalleryImage(location, imageID string, s.setVariable("apiVersion", communityGalleryApiVersion) // Required for DirectShared Gallery Image profile := resource.Properties.StorageProfile - profile.ImageReference = &compute.ImageReference{SharedGalleryImageID: &imageID} + profile.ImageReference = &hashiVMSDK.ImageReference{SharedGalleryImageId: &imageID} profile.OsDisk.OsType = s.osType profile.OsDisk.Vhd = nil profile.OsDisk.Caching = cachingType @@ -236,7 +242,7 @@ func (s *TemplateBuilder) SetDirectSharedGalleryImage(location, imageID string, return nil } -func (s *TemplateBuilder) SetMarketPlaceImage(publisher, offer, sku, version string, cachingType compute.CachingTypes) error { +func (s *TemplateBuilder) SetMarketPlaceImage(publisher, offer, sku, version string, cachingType hashiVMSDK.CachingTypes) error { resource, err := s.getResourceByType(resourceVirtualMachine) if err != nil { return err @@ -244,17 +250,17 @@ func (s *TemplateBuilder) SetMarketPlaceImage(publisher, offer, sku, version str 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), + profile.ImageReference = &hashiVMSDK.ImageReference{ + Publisher: common.StringPtr(publisher), + Offer: common.StringPtr(offer), + Sku: common.StringPtr(sku), + Version: common.StringPtr(version), } return nil } -func (s *TemplateBuilder) SetImageUrl(imageUrl string, osType compute.OperatingSystemTypes, cachingType compute.CachingTypes) error { +func (s *TemplateBuilder) SetImageUrl(imageUrl string, osType hashiVMSDK.OperatingSystemTypes, cachingType hashiVMSDK.CachingTypes) error { resource, err := s.getResourceByType(resourceVirtualMachine) if err != nil { return err @@ -264,8 +270,8 @@ func (s *TemplateBuilder) SetImageUrl(imageUrl string, osType compute.OperatingS profile.OsDisk.OsType = osType profile.OsDisk.Caching = cachingType - profile.OsDisk.Image = &compute.VirtualHardDisk{ - URI: to.StringPtr(imageUrl), + profile.OsDisk.Image = &hashiVMSDK.VirtualHardDisk{ + Uri: &imageUrl, } return nil @@ -274,15 +280,15 @@ func (s *TemplateBuilder) SetImageUrl(imageUrl string, osType compute.OperatingS func (s *TemplateBuilder) SetPlanInfo(name, product, publisher, promotionCode string) error { var promotionCodeVal *string = nil if promotionCode != "" { - promotionCodeVal = to.StringPtr(promotionCode) + promotionCodeVal = common.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), + Name: common.StringPtr(name), + Product: common.StringPtr(product), + Publisher: common.StringPtr(publisher), PromotionCode: promotionCodeVal, } } @@ -298,7 +304,7 @@ func (s *TemplateBuilder) SetOSDiskSizeGB(diskSizeGB int32) error { } profile := resource.Properties.StorageProfile - profile.OsDisk.DiskSizeGB = to.Int32Ptr(diskSizeGB) + profile.OsDisk.DiskSizeGB = common.Int32Ptr(diskSizeGB) return nil } @@ -310,14 +316,14 @@ func (s *TemplateBuilder) SetDiskEncryptionSetID(diskEncryptionSetID string) err } profile := resource.Properties.StorageProfile - profile.OsDisk.ManagedDisk.DiskEncryptionSet = &compute.DiskEncryptionSetParameters{ + profile.OsDisk.ManagedDisk.DiskEncryptionSet = &DiskEncryptionSetParameters{ ID: &diskEncryptionSetID, } return nil } -func (s *TemplateBuilder) SetAdditionalDisks(diskSizeGB []int32, dataDiskname string, isLegacyVHD bool, cachingType compute.CachingTypes) error { +func (s *TemplateBuilder) SetAdditionalDisks(diskSizeGB []int32, dataDiskname string, isLegacyVHD bool, cachingType hashiVMSDK.CachingTypes) error { resource, err := s.getResourceByType(resourceVirtualMachine) if err != nil { return err @@ -327,18 +333,18 @@ func (s *TemplateBuilder) SetAdditionalDisks(diskSizeGB []int32, dataDiskname st dataDisks := make([]DataDiskUnion, len(diskSizeGB)) for i, additionalSize := range diskSizeGB { - dataDisks[i].DiskSizeGB = to.Int32Ptr(additionalSize) - dataDisks[i].Lun = to.IntPtr(i) + dataDisks[i].DiskSizeGB = common.Int32Ptr(additionalSize) + dataDisks[i].Lun = common.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].Name = common.StringPtr(fmt.Sprintf("[concat(parameters('dataDiskName'),'-%d')]", i+1)) dataDisks[i].CreateOption = "Empty" dataDisks[i].Caching = cachingType if !isLegacyVHD { 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].Vhd = &hashiVMSDK.VirtualHardDisk{ + Uri: common.StringPtr(fmt.Sprintf("[concat(parameters('storageAccountBlobEndpoint'),variables('vmStorageAccountContainerName'),'/',parameters('dataDiskName'),'-%d','.vhd')]", i+1)), } dataDisks[i].ManagedDisk = nil } @@ -347,13 +353,13 @@ func (s *TemplateBuilder) SetAdditionalDisks(diskSizeGB []int32, dataDiskname st return nil } -func (s *TemplateBuilder) SetSpot(policy compute.VirtualMachineEvictionPolicyTypes, price float32) error { +func (s *TemplateBuilder) SetSpot(policy hashiVMSDK.VirtualMachineEvictionPolicyTypes, price float32) error { resource, err := s.getResourceByType(resourceVirtualMachine) if err != nil { return err } - resource.Properties.Priority = to.StringPtr("Spot") + resource.Properties.Priority = common.StringPtr("Spot") resource.Properties.EvictionPolicy = &policy if price == 0 { price = -1 @@ -369,7 +375,7 @@ func (s *TemplateBuilder) SetCustomData(customData string) error { } profile := resource.Properties.OsProfile - profile.CustomData = to.StringPtr(customData) + profile.CustomData = common.StringPtr(customData) return nil } @@ -381,7 +387,7 @@ func (s *TemplateBuilder) SetUserData(userData string) error { return err } - resource.Properties.UserData = to.StringPtr(userData) + resource.Properties.UserData = common.StringPtr(userData) return nil } @@ -403,7 +409,7 @@ func (s *TemplateBuilder) SetVirtualNetwork(virtualNetworkResourceGroup, virtual strings.Contains(s, "Microsoft.Network/publicIPAddresses") }) - (*resource.Properties.IPConfigurations)[0].PublicIPAddress = nil + (*resource.Properties.IPConfigurations)[0].Properties.PublicIPAddress = nil return nil } @@ -444,14 +450,14 @@ func (s *TemplateBuilder) SetNetworkSecurityGroup(ipAddresses []string, port int 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.Properties == nil { + subnet.Properties = &hashiSubnetsSDK.SubnetPropertiesFormat{} } - if subnet.SubnetPropertiesFormat.NetworkSecurityGroup != nil { + if subnet.Properties.NetworkSecurityGroup != nil { return fmt.Errorf("template: subnet already has an associated network security group") } - subnet.SubnetPropertiesFormat.NetworkSecurityGroup = &network.SecurityGroup{ - ID: to.StringPtr(resourceId), + subnet.Properties.NetworkSecurityGroup = &hashiSubnetsSDK.NetworkSecurityGroup{ + Id: common.StringPtr(resourceId), } err = s.addResource(vnetResource) @@ -484,7 +490,7 @@ func (s *TemplateBuilder) SetBootDiagnostics(diagSTG string) error { stg := fmt.Sprintf("https://%s.blob.core.windows.net", diagSTG) resource.Properties.DiagnosticsProfile.BootDiagnostics.Enabled = &t - resource.Properties.DiagnosticsProfile.BootDiagnostics.StorageURI = &stg + resource.Properties.DiagnosticsProfile.BootDiagnostics.StorageUri = &stg return nil } @@ -495,7 +501,7 @@ func (s *TemplateBuilder) SetLicenseType(licenseType string) error { return err } - resource.Properties.LicenseType = to.StringPtr(licenseType) + resource.Properties.LicenseType = common.StringPtr(licenseType) return nil } @@ -507,14 +513,15 @@ func (s *TemplateBuilder) SetSecurityProfile(secureBootEnabled bool, vtpmEnabled return err } - resource.Properties.SecurityProfile = &compute.SecurityProfile{} + resource.Properties.SecurityProfile = &hashiVMSDK.SecurityProfile{} + securityTrustedLaunch := hashiVMSDK.SecurityTypesTrustedLaunch if secureBootEnabled || vtpmEnabled { - resource.Properties.SecurityProfile.UefiSettings = &compute.UefiSettings{} - resource.Properties.SecurityProfile.SecurityType = compute.SecurityTypesTrustedLaunch - resource.Properties.SecurityProfile.UefiSettings.SecureBootEnabled = to.BoolPtr(secureBootEnabled) - resource.Properties.SecurityProfile.UefiSettings.VTpmEnabled = to.BoolPtr(vtpmEnabled) + resource.Properties.SecurityProfile.UefiSettings = &hashiVMSDK.UefiSettings{} + resource.Properties.SecurityProfile.SecurityType = &securityTrustedLaunch + resource.Properties.SecurityProfile.UefiSettings.SecureBootEnabled = common.BoolPtr(secureBootEnabled) + resource.Properties.SecurityProfile.UefiSettings.VTpmEnabled = common.BoolPtr(vtpmEnabled) } - resource.Properties.SecurityProfile.EncryptionAtHost = to.BoolPtr(encryptionAtHost) + resource.Properties.SecurityProfile.EncryptionAtHost = common.BoolPtr(encryptionAtHost) return nil } @@ -534,7 +541,7 @@ func (s *TemplateBuilder) ToJSON() (*string, error) { if err != nil { return nil, err } - return to.StringPtr(string(bs)), err + return common.StringPtr(string(bs)), err } func (s *TemplateBuilder) getResourceByType(t string) (*Resource, error) { @@ -607,24 +614,24 @@ func (s *TemplateBuilder) deleteResourceDependency(resource *Resource, predicate 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')]"), + ApiVersion: common.StringPtr("[variables('networkSecurityGroupsApiVersion')]"), + Name: common.StringPtr("[parameters('nsgName')]"), + Type: common.StringPtr(resourceNetworkSecurityGroups), + Location: common.StringPtr("[variables('location')]"), Properties: &Properties{ - SecurityRules: &[]network.SecurityRule{ + SecurityRules: &[]hashiSecurityRulesSDK.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, + Name: common.StringPtr("AllowIPsToSshWinRMInbound"), + Properties: &hashiSecurityRulesSDK.SecurityRulePropertiesFormat{ + Description: common.StringPtr("Allow inbound traffic from specified IP addresses"), + Protocol: hashiSecurityRulesSDK.SecurityRuleProtocolTcp, + Priority: 100, + Access: hashiSecurityRulesSDK.SecurityRuleAccessAllow, + Direction: hashiSecurityRulesSDK.SecurityRuleDirectionInbound, SourceAddressPrefixes: &srcIpAddresses, - SourcePortRange: to.StringPtr("*"), - DestinationAddressPrefix: to.StringPtr("VirtualNetwork"), - DestinationPortRange: to.StringPtr(strconv.Itoa(port)), + SourcePortRange: common.StringPtr("*"), + DestinationAddressPrefix: common.StringPtr("VirtualNetwork"), + DestinationPortRange: common.StringPtr(strconv.Itoa(port)), }, }, }, diff --git a/builder/azure/common/template/template_builder_test.TestBuildEncryptedWindows.approved.json b/builder/azure/common/template/template_builder_test.TestBuildEncryptedWindows.approved.json index d11c5442..04b196bc 100644 --- a/builder/azure/common/template/template_builder_test.TestBuildEncryptedWindows.approved.json +++ b/builder/azure/common/template/template_builder_test.TestBuildEncryptedWindows.approved.json @@ -151,7 +151,7 @@ "listeners": [ { "certificateUrl": "--test-winrm-certificate-url--", - "protocol": "https" + "protocol": "Https" } ] } diff --git a/builder/azure/common/template/template_builder_test.TestBuildWindows00.approved.json b/builder/azure/common/template/template_builder_test.TestBuildWindows00.approved.json index 97526163..4c58556c 100644 --- a/builder/azure/common/template/template_builder_test.TestBuildWindows00.approved.json +++ b/builder/azure/common/template/template_builder_test.TestBuildWindows00.approved.json @@ -151,7 +151,7 @@ "listeners": [ { "certificateUrl": "--test-winrm-certificate-url--", - "protocol": "https" + "protocol": "Https" } ] } diff --git a/builder/azure/common/template/template_builder_test.TestBuildWindows01.approved.json b/builder/azure/common/template/template_builder_test.TestBuildWindows01.approved.json index 60a24158..08596624 100644 --- a/builder/azure/common/template/template_builder_test.TestBuildWindows01.approved.json +++ b/builder/azure/common/template/template_builder_test.TestBuildWindows01.approved.json @@ -151,7 +151,7 @@ "listeners": [ { "certificateUrl": "--test-winrm-certificate-url--", - "protocol": "https" + "protocol": "Https" } ] } diff --git a/builder/azure/common/template/template_builder_test.TestBuildWindows02.approved.json b/builder/azure/common/template/template_builder_test.TestBuildWindows02.approved.json index 20c5c80b..a1659e5b 100644 --- a/builder/azure/common/template/template_builder_test.TestBuildWindows02.approved.json +++ b/builder/azure/common/template/template_builder_test.TestBuildWindows02.approved.json @@ -151,7 +151,7 @@ "listeners": [ { "certificateUrl": "--test-winrm-certificate-url--", - "protocol": "https" + "protocol": "Https" } ] } diff --git a/builder/azure/common/template/template_builder_test.go b/builder/azure/common/template/template_builder_test.go index 182700f1..517f5131 100644 --- a/builder/azure/common/template/template_builder_test.go +++ b/builder/azure/common/template/template_builder_test.go @@ -6,8 +6,8 @@ package template import ( "testing" - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2021-11-01/compute" approvaltests "github.com/approvals/go-approval-tests" + compute "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-01/virtualmachines" "github.com/hashicorp/packer-plugin-azure/builder/azure/common/constants" ) diff --git a/docs-partials/builder/azure/arm/Spot-not-required.mdx b/docs-partials/builder/azure/arm/Spot-not-required.mdx index 55c9fe5e..d5671d16 100644 --- a/docs-partials/builder/azure/arm/Spot-not-required.mdx +++ b/docs-partials/builder/azure/arm/Spot-not-required.mdx @@ -1,6 +1,6 @@ -- `eviction_policy` (compute.VirtualMachineEvictionPolicyTypes) - Specify eviction policy for spot instance: "Deallocate" or "Delete". If this is set, a spot instance will be used. +- `eviction_policy` (hashiVMSDK.VirtualMachineEvictionPolicyTypes) - Specify eviction policy for spot instance: "Deallocate" or "Delete". If this is set, a spot instance will be used. - `max_price` (float32) - How much should the VM cost maximally per hour. Specify -1 (or do not specify) to not evict based on price. diff --git a/example/oidc-example.json b/example/oidc-example.json new file mode 100644 index 00000000..5b125cfc --- /dev/null +++ b/example/oidc-example.json @@ -0,0 +1,28 @@ +{ + "variables": { + "arm_oidc_token": "{{env `ARM_OIDC_TOKEN`}}", + "subscription_id": "{{env `ARM_SUBSCRIPTION_ID`}}", + "arm_client_id": "{{env `ARM_CLIENT_ID`}}" + }, + "builders": [{ + "type": "azure-arm", + "managed_image_resource_group_name": "packer-acceptance-test", + "managed_image_name": "oidc-example", + "os_type": "Windows", + "image_publisher": "MicrosoftWindowsServer", + "image_offer": "WindowsServer", + "image_sku": "2012-R2-Datacenter", + "client_jwt": "{{user `arm_oidc_token`}}", + "client_id": "{{user `arm_client_id`}}", + "subscription_id": "{{ user `subscription_id`}}", + "communicator": "winrm", + "winrm_use_ssl": "true", + "winrm_insecure": "true", + "winrm_timeout": "3m", + "winrm_username": "packer", + "location": "South Central US", + "vm_size": "Standard_DS2_v2" + }] +} + + diff --git a/example/oidc-example.pkr.hcl b/example/oidc-example.pkr.hcl new file mode 100644 index 00000000..802a34cc --- /dev/null +++ b/example/oidc-example.pkr.hcl @@ -0,0 +1,39 @@ + +variable "arm_client_id" { + type = string + default = "${env("ARM_CLIENT_ID")}" +} + +variable "arm_oidc_token" { + type = string + default = "${env("ARM_OIDC_TOKEN")}" +} + +variable "subscription_id" { + type = string + default = "${env("ARM_SUBSCRIPTION_ID")}" +} + +source "azure-arm" "autogenerated_1" { + client_id = "${var.arm_client_id}" + client_jwt = "${var.arm_oidc_token}" + communicator = "winrm" + image_offer = "WindowsServer" + image_publisher = "MicrosoftWindowsServer" + image_sku = "2012-R2-Datacenter" + location = "South Central US" + managed_image_name = "oidc-example" + managed_image_resource_group_name = "packer-acceptance-test" + os_type = "Windows" + subscription_id = "${var.subscription_id}" + vm_size = "Standard_DS2_v2" + winrm_insecure = "true" + winrm_timeout = "3m" + winrm_use_ssl = "true" + winrm_username = "packer" +} + +build { + sources = ["source.azure-arm.autogenerated_1"] + +} diff --git a/go.mod b/go.mod index 959c32b0..662bf36c 100644 --- a/go.mod +++ b/go.mod @@ -3,32 +3,38 @@ module github.com/hashicorp/packer-plugin-azure go 1.19 require ( - github.com/Azure/azure-sdk-for-go v64.0.0+incompatible - github.com/Azure/go-autorest/autorest v0.11.19 - github.com/Azure/go-autorest/autorest/adal v0.9.20 + github.com/Azure/azure-sdk-for-go v66.0.0+incompatible + github.com/Azure/go-autorest/autorest v0.11.29 + github.com/Azure/go-autorest/autorest/adal v0.9.23 github.com/Azure/go-autorest/autorest/azure/auth v0.4.2 - github.com/Azure/go-autorest/autorest/azure/cli v0.4.2 - github.com/Azure/go-autorest/autorest/date v0.3.0 + github.com/Azure/go-autorest/autorest/azure/cli v0.4.4 + github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect github.com/Azure/go-autorest/autorest/to v0.4.0 github.com/approvals/go-approval-tests v0.0.0-20210131072903-38d0b0ec12b1 github.com/dnaeon/go-vcr v1.1.0 // indirect github.com/golang-jwt/jwt v3.2.2+incompatible github.com/google/go-cmp v0.5.9 - github.com/hashicorp/go-azure-helpers v0.16.5 + github.com/hashicorp/go-azure-helpers v0.56.0 github.com/hashicorp/hcl/v2 v2.16.2 - github.com/hashicorp/packer-plugin-sdk v0.5.1 + github.com/hashicorp/packer-plugin-sdk v0.4.0 github.com/masterzen/winrm v0.0.0-20210623064412-3b76017826b0 - github.com/mitchellh/mapstructure v1.4.1 - github.com/mitchellh/reflectwalk v1.0.0 - github.com/stretchr/testify v1.7.0 - github.com/zclconf/go-cty v1.12.1 - golang.org/x/crypto v0.0.0-20220517005047-85d78b3ac167 + github.com/mitchellh/mapstructure v1.5.0 + github.com/mitchellh/reflectwalk v1.0.2 + github.com/stretchr/testify v1.8.2 + github.com/zclconf/go-cty v1.13.1 + golang.org/x/crypto v0.9.0 +) + +require ( + github.com/dimchansky/utfbom v1.1.1 + github.com/hashicorp/go-azure-sdk v0.20230523.1140858 + github.com/mitchellh/go-homedir v1.1.0 + github.com/tombuildsstuff/giovanni v0.20.0 ) require ( cloud.google.com/go v0.105.0 // indirect - cloud.google.com/go/compute v1.12.1 // indirect - cloud.google.com/go/compute/metadata v0.1.1 // indirect + cloud.google.com/go/compute/metadata v0.2.0 // indirect cloud.google.com/go/iam v0.6.0 // indirect cloud.google.com/go/storage v1.27.0 // indirect github.com/Azure/go-autorest v14.2.0+incompatible // indirect @@ -38,41 +44,48 @@ require ( github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c // indirect github.com/ChrisTrenkamp/goxpath v0.0.0-20210404020558-97928f7e12b6 // indirect github.com/agext/levenshtein v1.2.3 // indirect + github.com/apparentlymart/go-dump v0.0.0-20190214190832-042adf3cf4a0 // indirect github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect github.com/armon/go-metrics v0.3.9 // indirect github.com/aws/aws-sdk-go v1.44.114 // indirect github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect github.com/cenkalti/backoff/v3 v3.2.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/dimchansky/utfbom v1.1.1 // indirect github.com/dylanmei/iso8601 v0.1.0 // indirect - github.com/fatih/color v1.12.0 // indirect + github.com/fatih/color v1.13.0 // indirect github.com/gofrs/flock v0.8.1 // indirect github.com/gofrs/uuid v4.0.0+incompatible // indirect - github.com/golang-jwt/jwt/v4 v4.2.0 // indirect + github.com/golang-jwt/jwt/v4 v4.5.0 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.2 // indirect github.com/golang/snappy v0.0.4 // indirect + github.com/google/btree v1.0.0 // indirect github.com/google/uuid v1.3.0 // indirect github.com/googleapis/enterprise-certificate-proxy v0.2.0 // indirect github.com/googleapis/gax-go/v2 v2.6.0 // indirect github.com/hashicorp/consul/api v1.10.1 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect + github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320 // indirect github.com/hashicorp/go-getter/gcs/v2 v2.2.0 // indirect github.com/hashicorp/go-getter/s3/v2 v2.2.0 // indirect github.com/hashicorp/go-getter/v2 v2.2.0 // indirect - github.com/hashicorp/go-hclog v0.16.2 // indirect + github.com/hashicorp/go-hclog v1.4.0 // indirect github.com/hashicorp/go-immutable-radix v1.3.1 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect - github.com/hashicorp/go-retryablehttp v0.7.0 // indirect + github.com/hashicorp/go-retryablehttp v0.7.2 // indirect github.com/hashicorp/go-rootcerts v1.0.2 // indirect github.com/hashicorp/go-safetemp v1.0.0 // indirect github.com/hashicorp/go-sockaddr v1.0.2 // indirect + github.com/hashicorp/go-uuid v1.0.3 // indirect github.com/hashicorp/go-version v1.6.0 // indirect github.com/hashicorp/golang-lru v0.5.4 // indirect github.com/hashicorp/hcl v1.0.0 // indirect + github.com/hashicorp/logutils v1.0.0 // indirect github.com/hashicorp/serf v0.9.5 // indirect + github.com/hashicorp/terraform-plugin-go v0.14.3 // indirect + github.com/hashicorp/terraform-plugin-log v0.8.0 // indirect + github.com/hashicorp/terraform-plugin-sdk/v2 v2.26.1 // indirect github.com/hashicorp/vault/api v1.1.1 // indirect github.com/hashicorp/vault/sdk v0.2.1 // indirect github.com/hashicorp/yamux v0.0.0-20210826001029-26ff87cf9493 // indirect @@ -80,11 +93,12 @@ require ( github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/klauspost/compress v1.11.2 // indirect github.com/kr/fs v0.1.0 // indirect + github.com/kr/pretty v0.2.1 // indirect github.com/masterzen/simplexml v0.0.0-20190410153822-31eea3082786 // indirect - github.com/mattn/go-colorable v0.1.8 // indirect - github.com/mattn/go-isatty v0.0.13 // indirect + github.com/mattn/go-colorable v0.1.12 // indirect + github.com/mattn/go-isatty v0.0.14 // indirect + github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/go-fs v0.0.0-20180402235330-b7b9ca407fff // indirect - github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/go-testing-interface v1.14.1 // indirect github.com/mitchellh/go-wordwrap v1.0.1 // indirect github.com/mitchellh/iochan v1.0.0 // indirect @@ -96,22 +110,27 @@ require ( github.com/ryanuber/go-glob v1.0.0 // indirect github.com/ugorji/go/codec v1.2.6 // indirect github.com/ulikunitz/xz v0.5.10 // indirect + github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect + github.com/vmihailenco/msgpack/v4 v4.3.12 // indirect + github.com/vmihailenco/tagparser v0.1.1 // indirect go.opencensus.io v0.23.0 // indirect - golang.org/x/net v0.8.0 // indirect - golang.org/x/oauth2 v0.1.0 // indirect - golang.org/x/sys v0.6.0 // indirect - golang.org/x/term v0.6.0 // indirect - golang.org/x/text v0.8.0 // indirect + golang.org/x/net v0.10.0 // indirect + golang.org/x/oauth2 v0.4.0 // indirect + golang.org/x/sys v0.8.0 // indirect + golang.org/x/term v0.8.0 // indirect + golang.org/x/text v0.9.0 // indirect golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect google.golang.org/api v0.101.0 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto v0.0.0-20221027153422-115e99e71e1c // indirect - google.golang.org/grpc v1.50.1 // indirect + google.golang.org/grpc v1.51.0 // indirect google.golang.org/protobuf v1.28.1 // indirect + gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/square/go-jose.v2 v2.6.0 // indirect gopkg.in/yaml.v2 v2.3.0 // indirect - gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect + software.sslmate.com/src/go-pkcs12 v0.2.0 // indirect ) // Incorrect plugin registration for Azure component; see hashicorp/packer-plugin-azure/pull/73 diff --git a/go.sum b/go.sum index c376ccfc..a505f8c9 100644 --- a/go.sum +++ b/go.sum @@ -2,38 +2,43 @@ bazil.org/fuse v0.0.0-20160811212531-371fbbdaa898/go.mod h1:Xbm+BRKSBEpa4q4hTSxo cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.105.0 h1:DNtEKRBAAzeS4KyIory52wWHuClNaXJ5x1F7xa4q+5Y= cloud.google.com/go v0.105.0/go.mod h1:PrLgOJNe5nfE9UMxKxgXj4mD3voiP+YQ6gdt6KMFOKM= -cloud.google.com/go/compute v1.12.1 h1:gKVJMEyqV5c/UnpzjjQbo3Rjvvqpr9B1DFSbJC4OXr0= -cloud.google.com/go/compute v1.12.1/go.mod h1:e8yNOBcBONZU1vJKCvCoDw/4JQsA0dpM4x/6PIIOocU= -cloud.google.com/go/compute/metadata v0.1.1 h1:/sxEbyrm6cw+XOUw1YxBHlatV71z4vpnmO7z2IZ0h3I= -cloud.google.com/go/compute/metadata v0.1.1/go.mod h1:Z1VN+bulIf6bt4P/C37K4DyZYZEXYonfTBHHFPO/4UU= +cloud.google.com/go/compute/metadata v0.2.0 h1:nBbNSZyDpkNlo3DepaaLKVuO7ClyifSAmNloSCZrHnQ= +cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= cloud.google.com/go/iam v0.6.0 h1:nsqQC88kT5Iwlm4MeNGTpfMWddp6NB/UOLFTH6m1QfQ= cloud.google.com/go/iam v0.6.0/go.mod h1:+1AH33ueBne5MzYccyMHtEKqLE4/kJOibtffMHDMFMc= cloud.google.com/go/longrunning v0.1.1 h1:y50CXG4j0+qvEukslYFBCrzaXX0qpFbBzc3PchSu/LE= cloud.google.com/go/storage v1.27.0 h1:YOO045NZI9RKfCj1c5A/ZtuuENUc8OAW+gHdGnDgyMQ= cloud.google.com/go/storage v1.27.0/go.mod h1:x9DOL8TK/ygDUMieqwfhdpQryTeEkhGKMi80i/iqR2s= -github.com/Azure/azure-sdk-for-go v51.2.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= -github.com/Azure/azure-sdk-for-go v64.0.0+incompatible h1:WAA77WBDWYtNfCC95V70VvkdzHe+wM/r2MQ9mG7fnQs= -github.com/Azure/azure-sdk-for-go v64.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= +github.com/Azure/azure-sdk-for-go v45.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= +github.com/Azure/azure-sdk-for-go v56.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= +github.com/Azure/azure-sdk-for-go v66.0.0+incompatible h1:bmmC38SlE8/E81nNADlgmVGurPWMHDX2YNXVQMrBpEE= +github.com/Azure/azure-sdk-for-go v66.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= 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.11.18/go.mod h1:dSiJPy22c3u0OtOKDNttNgqpNFY/GeWa7GH/Pz56QRA= -github.com/Azure/go-autorest/autorest v0.11.19 h1:7/IqD2fEYVha1EPeaiytVKhzmPV223pfkRIQUGOK2IE= +github.com/Azure/go-autorest/autorest v0.11.3/go.mod h1:JFgpikqFJ/MleTTxwepExTKnFUKKszPS8UavbQYUMuw= github.com/Azure/go-autorest/autorest v0.11.19/go.mod h1:dSiJPy22c3u0OtOKDNttNgqpNFY/GeWa7GH/Pz56QRA= +github.com/Azure/go-autorest/autorest v0.11.29 h1:I4+HL/JDvErx2LjyzaVxllw2lRDB5/BT2Bm4g20iqYw= +github.com/Azure/go-autorest/autorest v0.11.29/go.mod h1:ZtEzC4Jy2JDrZLxvWs8LrBWEBycl1hbT1eknI8MtfAs= 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.9.0/go.mod h1:/c022QCutn2P7uY+/oQWWNcK9YU+MH96NgK+jErpbcg= 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/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M= -github.com/Azure/go-autorest/autorest/adal v0.9.20 h1:gJ3E98kMpFB1MFqQCvA1yFab8vthOeD4VlFRQULxahg= -github.com/Azure/go-autorest/autorest/adal v0.9.20/go.mod h1:XVVeme+LZwABT8K5Lc3hA4nAe8LDBVle26gTrguhhPQ= +github.com/Azure/go-autorest/autorest/adal v0.9.14/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M= +github.com/Azure/go-autorest/autorest/adal v0.9.22/go.mod h1:XuAbAEUv2Tta//+voMI038TrJBqjKam0me7qR+L8Cmk= +github.com/Azure/go-autorest/autorest/adal v0.9.23 h1:Yepx8CvFxwNKpH6ja7RZ+sKX+DWYNldbLiALMC3BTz8= +github.com/Azure/go-autorest/autorest/adal v0.9.23/go.mod h1:5pcMqFkdPhviJdlEy3kC/v1ZLnQl0MH6XA5YCcMhy4c= 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/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.0/go.mod h1:JljT387FplPzBA31vUcvsetLKF3pec5bdAxjVU4kI2s= github.com/Azure/go-autorest/autorest/azure/cli v0.4.2/go.mod h1:7qkJkT+j6b+hIpzMOwPChJhTqS8VbsqqgULzMNRugoM= +github.com/Azure/go-autorest/autorest/azure/cli v0.4.4 h1:iuooz5cZL6VRcO7DVSFYxRcouqn6bFVE/e77Wts50Zk= +github.com/Azure/go-autorest/autorest/azure/cli v0.4.4/go.mod h1:yAQ2b6eP/CmLPnmLvxtT1ALIY3OR1oFcCqVBi8vHiTc= 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/go.mod h1:vcORJHLJEh643/Ioh9+vPmf1Ij9AEBM5FuBIXLmIy0g= github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw= @@ -41,13 +46,17 @@ github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSY 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/go.mod h1:a8FDP3DYzQ4RYfVAxAN3SVSiiO77gL2j2ronKKP0syM= -github.com/Azure/go-autorest/autorest/mocks v0.4.1 h1:K0laFcLE6VLTOwNgSxaGbUcLPuGXlNkbVvq4cW4nIHk= +github.com/Azure/go-autorest/autorest/mocks v0.4.0/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= +github.com/Azure/go-autorest/autorest/mocks v0.4.2 h1:PGN4EDXnuQbojHbU0UWoNvmu9AGVwYHG9/fkDYhtAfw= +github.com/Azure/go-autorest/autorest/mocks v0.4.2/go.mod h1:Vy7OitM9Kei0i1Oj+LvyAWMXJHeKH1MVlzFugfVrmyU= 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.3.0/go.mod h1:yhLgjC0Wda5DYXl6JAsWyUe4KVNffhoDhG0zVzUMo3E= 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/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc= +github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= 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= @@ -71,7 +80,9 @@ github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRF github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/antchfx/xpath v1.1.11 h1:WOFtK8TVAjLm3lbgqeP0arlHpvCEeTANeWZ/csPpJkQ= github.com/antchfx/xquery v0.0.0-20180515051857-ad5b8c7a47b0 h1:JaCC8jz0zdMLk2m+qCCVLLLM/PL93p84w4pK3aJWj60= -github.com/apparentlymart/go-dump v0.0.0-20180507223929-23540a00eaa3 h1:ZSTrOEhiM5J5RFxEaFvMZVEAM1KvT1YzbEOwB2EAGjA= +github.com/apparentlymart/go-dump v0.0.0-20190214190832-042adf3cf4a0 h1:MzVXffFUye+ZcSR6opIgz9Co7WcDx6ZcY+RjfFHoA0I= +github.com/apparentlymart/go-dump v0.0.0-20190214190832-042adf3cf4a0/go.mod h1:oL81AME2rN47vu18xqj1S1jPIPuN7afo62yKTNn3XMM= +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-20210131072903-38d0b0ec12b1 h1:uroQ0JaeVom9Ffv9xFtc7DcqrpGmyQeZCRzHD9FqPBg= @@ -138,10 +149,11 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= -github.com/fatih/color v1.12.0 h1:mRhaKNwANqRgUBGKmnI5ZxEk7QXmjQeCcuYFMX2bfcc= -github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= +github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= +github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= +github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/frankban/quicktest v1.10.0 h1:Gfh+GAJZOAoKZsIZeZbdn2JF10kN1XHNvjsvQK8gVkE= github.com/frankban/quicktest v1.10.0/go.mod h1:ui7WezCLWMWxVWr1GETZY3smRy0G4KWq9vcPtJmFl7Y= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= @@ -169,13 +181,14 @@ github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXP github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= -github.com/golang-jwt/jwt/v4 v4.2.0 h1:besgBTC8w8HjP6NzQdxwKH9Z5oQMZ24ThTrHp3cZ8eU= -github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= +github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= +github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -195,8 +208,9 @@ github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiu github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c h1:964Od4U6p2jUkFxvCydnIczKteheJEzHRToSGK3Bnlw= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= @@ -208,6 +222,7 @@ github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian/v3 v3.2.1 h1:d8MncMlErDFTwQGBK1xhv026j9kqhvw1Qv9IbWT1VLQ= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -223,12 +238,17 @@ github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOj github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/go-azure-helpers v0.16.5 h1:rhcpg2Nk1Li6173KTHNnj/i+MGXYNjvYKIdNlg6q3RU= -github.com/hashicorp/go-azure-helpers v0.16.5/go.mod h1:kR7+sTDEb9TOp/O80ss1UEJg1t4/BHLD/U8wHLS4BGQ= +github.com/hashicorp/go-azure-helpers v0.12.0/go.mod h1:Zc3v4DNeX6PDdy7NljlYpnrdac1++qNW0I4U+ofGwpg= +github.com/hashicorp/go-azure-helpers v0.56.0 h1:KxDXISHwWe4PKEz6FSSPG8vCXNrFGsCcCn/AI94ccig= +github.com/hashicorp/go-azure-helpers v0.56.0/go.mod h1:MbnCV9jPmlkbdH7VsoBK8IbCHvo3pLWRbRvq+F6u9sI= +github.com/hashicorp/go-azure-sdk v0.20230523.1140858 h1:pstzTqIYBr2XnthdjCen/1DefYSGls6hSfQqYGWMuZo= +github.com/hashicorp/go-azure-sdk v0.20230523.1140858/go.mod h1:x2r7/U5MKlTHUO6/hFHRNO03qkRLBYyeQOuEUHkZmEg= github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= +github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320 h1:1/D3zfFHttUKaCaGKZ/dR2roBXv0vKbSCnssIldfQdI= +github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320/go.mod h1:EiZBMaudVLy8fmjf9Npq1dq9RalhveqZG5w/yz3mHWs= github.com/hashicorp/go-getter/gcs/v2 v2.2.0 h1:oo6LVXva9I4CZzv531mn/FY5gtRx540I6eWwJVPWA4o= github.com/hashicorp/go-getter/gcs/v2 v2.2.0/go.mod h1:oDHNhZN2Gf6UqfIol4S9PkbzMFlf8rBctrJvAHDTtOM= github.com/hashicorp/go-getter/s3/v2 v2.2.0 h1:JGZihrdkZnuw+bDf8BnxuRLPwi0cfL2mwRlWA2lREAg= @@ -240,8 +260,8 @@ github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrj github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= github.com/hashicorp/go-hclog v0.14.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= github.com/hashicorp/go-hclog v0.16.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= -github.com/hashicorp/go-hclog v0.16.2 h1:K4ev2ib4LdQETX5cSZBG0DVLk1jwGqSPXBjdah3veNs= -github.com/hashicorp/go-hclog v0.16.2/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= +github.com/hashicorp/go-hclog v1.4.0 h1:ctuWFGrhFha8BnnzxqeRGidlEcQkDyL5u8J8t5eA11I= +github.com/hashicorp/go-hclog v1.4.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-immutable-radix v1.1.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc= @@ -257,8 +277,8 @@ github.com/hashicorp/go-plugin v1.0.1/go.mod h1:++UyYGoz3o5w9ZzAdZxtQKrWWP+iqPBn github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= github.com/hashicorp/go-retryablehttp v0.6.2/go.mod h1:gEx6HMUGxYYhJScX7W1Il64m6cc2C1mDaW3NQ9sY1FY= github.com/hashicorp/go-retryablehttp v0.6.6/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= -github.com/hashicorp/go-retryablehttp v0.7.0 h1:eu1EI/mbirUgP5C8hVsTNaGZreBDlYiwC1FZWkvQPQ4= -github.com/hashicorp/go-retryablehttp v0.7.0/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= +github.com/hashicorp/go-retryablehttp v0.7.2 h1:AcYqCvkpalPnPF2pn0KamgwamS42TqUDDYFRKq/RAd0= +github.com/hashicorp/go-retryablehttp v0.7.2/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8= github.com/hashicorp/go-rootcerts v1.0.1/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc= github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= @@ -270,11 +290,11 @@ github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjG github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-uuid v1.0.2 h1:cfejS+Tpcp13yd5nYHWDI6qVCny6wyX2Mt5SGur2IGE= github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= +github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= 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.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek= github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= @@ -286,14 +306,21 @@ github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/hcl/v2 v2.16.2 h1:mpkHZh/Tv+xet3sy3F9Ld4FyI2tUpWe9x3XtPx9f1a0= github.com/hashicorp/hcl/v2 v2.16.2/go.mod h1:JRmR89jycNkrrqnMmvPDMd56n1rQJ2Q6KocSLCMCXng= +github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= github.com/hashicorp/mdns v1.0.1/go.mod h1:4gW7WsVCke5TE7EPeYliwHlRUyBtfCwuFwuMg2DmyNY= github.com/hashicorp/memberlist v0.2.2 h1:5+RffWKwqJ71YPu9mWsF7ZOscZmwfasdA8kbdC7AO2g= github.com/hashicorp/memberlist v0.2.2/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= -github.com/hashicorp/packer-plugin-sdk v0.5.1 h1:ucXGWru98LsQrAWq8cEnaNN2Dvqw0HtyupAwNzrfy44= -github.com/hashicorp/packer-plugin-sdk v0.5.1/go.mod h1:OKziA4fQodpIq5HzOpRNt+vLpRJae61Z4uVFbeMLgd8= +github.com/hashicorp/packer-plugin-sdk v0.4.0 h1:UyLYe0y02D9wkOQ3FeeZWyFg2+mx2vLuWRGUL5xt50I= +github.com/hashicorp/packer-plugin-sdk v0.4.0/go.mod h1:uNhU3pmjM2ejgHYce/g4J+sa5rh81iYQztpGvGa5FOs= github.com/hashicorp/serf v0.9.5 h1:EBWvyu9tcRszt3Bxp3KNssBMP1KuHWyO51lz9+786iM= github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk= +github.com/hashicorp/terraform-plugin-go v0.14.3 h1:nlnJ1GXKdMwsC8g1Nh05tK2wsC3+3BL/DBBxFEki+j0= +github.com/hashicorp/terraform-plugin-go v0.14.3/go.mod h1:7ees7DMZ263q8wQ6E4RdIdR6nHHJtrdt4ogX5lPkX1A= +github.com/hashicorp/terraform-plugin-log v0.8.0 h1:pX2VQ/TGKu+UU1rCay0OlzosNKe4Nz1pepLXj95oyy0= +github.com/hashicorp/terraform-plugin-log v0.8.0/go.mod h1:1myFrhVsBLeylQzYYEV17VVjtG8oYPRFdaZs7xdW2xs= +github.com/hashicorp/terraform-plugin-sdk/v2 v2.26.1 h1:G9WAfb8LHeCxu7Ae8nc1agZlQOSCUWsb610iAogBhCs= +github.com/hashicorp/terraform-plugin-sdk/v2 v2.26.1/go.mod h1:xcOSYlRVdPLmDUoqPhO9fiO/YCN/l6MGYeTzGt5jgkQ= github.com/hashicorp/vault/api v1.0.5-0.20200519221902-385fac77e20f/go.mod h1:euTFbi2YJgwcju3imEt919lhJKF68nN1cQPq3aA+kBE= github.com/hashicorp/vault/api v1.1.1 h1:907ld+Z9cALyvbZK2qUX9cLwvSaEQsMVQB3x2KE8+AI= github.com/hashicorp/vault/api v1.1.1/go.mod h1:29UXcn/1cLOPHQNMWA7bCz2By4PSd0VKPAydKXS5yN0= @@ -326,12 +353,14 @@ github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/masterzen/simplexml v0.0.0-20160608183007-4572e39b1ab9/go.mod h1:kCEbxUJlNDEBNbdQMkPSp6yaKcRXVI6f4ddk8Riv4bc= github.com/masterzen/simplexml v0.0.0-20190410153822-31eea3082786 h1:2ZKn+w/BJeL43sCxI2jhPLRv73oVVOjEKZjKkflyqxg= github.com/masterzen/simplexml v0.0.0-20190410153822-31eea3082786/go.mod h1:kCEbxUJlNDEBNbdQMkPSp6yaKcRXVI6f4ddk8Riv4bc= @@ -340,15 +369,16 @@ github.com/masterzen/winrm v0.0.0-20210623064412-3b76017826b0/go.mod h1:l31LCh9V github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8= -github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= +github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-isatty v0.0.13 h1:qdl+GuBjcsKKDco5BsxPJlId98mSWNKqYA+Co0SC1yA= -github.com/mattn/go-isatty v0.0.13/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.1.26 h1:gPxPSwALAeHJSjarOs00QjVdV9QoBvc1D2ujQUr5BzU= @@ -356,6 +386,8 @@ github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKju github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= +github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= +github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= github.com/mitchellh/go-fs v0.0.0-20180402235330-b7b9ca407fff h1:bFJ74ac7ZK/jyislqiWdzrnENesFt43sNEBRh1xk/+g= github.com/mitchellh/go-fs v0.0.0-20180402235330-b7b9ca407fff/go.mod h1:g7SZj7ABpStq3tM4zqHiVEG5un/DZ1+qJJKO7qx1EvU= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= @@ -372,10 +404,11 @@ github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0Qu github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.3.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag= -github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= 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/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= @@ -449,15 +482,23 @@ github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKv github.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/tombuildsstuff/giovanni v0.20.0 h1:IM/I/iNWMXnPYwcSq8uxV7TKDlv7Nejq0bRK9i6O/C0= +github.com/tombuildsstuff/giovanni v0.20.0/go.mod h1:66KVLYma2whJhEdxPSPL3GQHkulhK+C5CluKfHGfPF4= github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= github.com/ugorji/go v1.2.6/go.mod h1:anCg0y61KIhDlPZmnH+so+RQbysYVyDko0IMgJv0Nn0= github.com/ugorji/go/codec v1.2.6 h1:7kbGefxLoDBuYXOms4yD7223OpNMMPNPZxXk5TvFcyQ= @@ -465,8 +506,14 @@ github.com/ugorji/go/codec v1.2.6/go.mod h1:V6TCNZ4PHqoHGFZuSG1W8nrCzzdgA2DozYxW github.com/ulikunitz/xz v0.5.10 h1:t92gobL9l3HE202wg3rlk19F6X+JOxl9BBrCCMYEYd8= github.com/ulikunitz/xz v0.5.10/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= +github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= +github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaUXK79GlxNBwueZn0xI= +github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= +github.com/vmihailenco/msgpack/v4 v4.3.12 h1:07s4sz9IReOgdikxLTKNbBdqDMLsjPKXwvCazn8G65U= github.com/vmihailenco/msgpack/v4 v4.3.12/go.mod h1:gborTTJjAo/GWTqqRjrLCn9pgNN+NXzzngzBKDPIqw4= +github.com/vmihailenco/tagparser v0.1.1 h1:quXMXlA39OCbd2wAdTsGDlK9RkOk6Wuw+x37wVyIuWY= github.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= @@ -482,16 +529,20 @@ golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20220517005047-85d78b3ac167 h1:O8uGbHCqlTp2P6QJSLmCojM4mN6UemYv8K+dCnmHmu0= -golang.org/x/crypto v0.0.0-20220517005047-85d78b3ac167/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= +golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g= +golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -513,18 +564,22 @@ golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= -golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.1.0 h1:isLCZuhj4v+tYv7eskaN4v/TM+A1begWWgyVJDdl1+Y= -golang.org/x/oauth2 v0.1.0/go.mod h1:G9FE4dLTsbXUu90h/Pf85g4w1D+SSAgR+q46nJZ8M4A= +golang.org/x/oauth2 v0.4.0 h1:NF0gk8LVPg1Ml7SSbGyySuoxdsXitj7TvgvuRxIMc/M= +golang.org/x/oauth2 v0.4.0/go.mod h1:RznEsdpjGAINPTOF0UH/t+xJ75L18YO3Ho6Pyn+uRec= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -553,22 +608,31 @@ golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= +golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.6.0 h1:clScbb1cHjoCkyRbWwBEUZ5H/tIFu5TAXIqaZD0Gcjw= -golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.8.0 h1:n5xxQn2i3PC0yLAbjTpNT85q/Kgzcr2gIoX9OrJUols= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68= -golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac h1:7zkz7BUtwNFFqcowJ+RIgu2MaV/MapERkDIy+mwPyjs= @@ -583,6 +647,8 @@ golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBn golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk= @@ -611,8 +677,8 @@ google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQ google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.50.1 h1:DS/BukOZWp8s6p4Dt/tOaJaTQyPyOoCcrjroHuCeLzY= -google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= +google.golang.org/grpc v1.51.0 h1:E1eGv1FTqoLIdnBCZufiSHgKjlqG6fKFf6pPWtMTh8U= +google.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsAIPww= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -630,8 +696,9 @@ gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4 gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= @@ -647,9 +714,11 @@ gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +software.sslmate.com/src/go-pkcs12 v0.2.0 h1:nlFkj7bTysH6VkC4fGphtjXRbezREPgrHuJG20hBGPE= +software.sslmate.com/src/go-pkcs12 v0.2.0/go.mod h1:23rNcYsMabIc1otwLpTkCCPwUq6kQsTyowttG/as0kQ= From c46a7ce970b19a7d95bb9ffc552f4545d52a36f8 Mon Sep 17 00:00:00 2001 From: Jenna Goldstrich Date: Mon, 26 Jun 2023 12:17:00 -0700 Subject: [PATCH 02/15] Azure Germany was shutdown in 2021 --- builder/azure/common/client/config.go | 21 ++------------------- 1 file changed, 2 insertions(+), 19 deletions(-) diff --git a/builder/azure/common/client/config.go b/builder/azure/common/client/config.go index 30ad722f..af7d6562 100644 --- a/builder/azure/common/client/config.go +++ b/builder/azure/common/client/config.go @@ -32,7 +32,7 @@ import ( // Packer is running on an Azure VM with either a System Assigned Managed // Identity or User Assigned Managed Identity. type Config struct { - // One of Public, China, Germany, or + // One of Public, China, or // USGovernment. Defaults to Public. Long forms such as // USGovernmentCloud and AzureUSGovernmentCloud are also supported. CloudEnvironmentName string `mapstructure:"cloud_environment_name" required:"false"` @@ -138,16 +138,6 @@ func (c *Config) setNewCloudEnvironment() error { "CHINACLOUD": "china", "AZURECHINACLOUD": "china", - // TODO Implement AzureGermanCloud - // I reached out to the provider team and will try to get it implemented in the SDK first` - "GERMAN": "AzureGermanCloud", - "GERMANCLOUD": "AzureGermanCloud", - "AZUREGERMANCLOUD": "AzureGermanCloud", - - "GERMANY": "AzureGermanCloud", - "GERMANYCLOUD": "AzureGermanCloud", - "AZUREGERMANYCLOUD": "AzureGermanCloud", - "PUBLIC": "public", "PUBLICCLOUD": "public", "AZUREPUBLICCLOUD": "public", @@ -172,6 +162,7 @@ func (c *Config) setNewCloudEnvironment() error { return nil } +// This is still used by the Chroot and DTL builder func (c *Config) setCloudEnvironment() error { // First, try using the metadata host to look up the cloud. if c.MetadataHost == "" { @@ -192,14 +183,6 @@ func (c *Config) setCloudEnvironment() error { "CHINACLOUD": "AzureChinaCloud", "AZURECHINACLOUD": "AzureChinaCloud", - "GERMAN": "AzureGermanCloud", - "GERMANCLOUD": "AzureGermanCloud", - "AZUREGERMANCLOUD": "AzureGermanCloud", - - "GERMANY": "AzureGermanCloud", - "GERMANYCLOUD": "AzureGermanCloud", - "AZUREGERMANYCLOUD": "AzureGermanCloud", - "PUBLIC": "AzurePublicCloud", "PUBLICCLOUD": "AzurePublicCloud", "AZUREPUBLICCLOUD": "AzurePublicCloud", From 61152df7009292dd290d0853f9d04d727cf30425 Mon Sep 17 00:00:00 2001 From: Jenna Goldstrich Date: Mon, 26 Jun 2023 17:19:43 -0700 Subject: [PATCH 03/15] Remove Template Capture from ARM Builder, this work will need to be replicated to DTL/Chroot --- builder/azure/arm/artifact.go | 86 +----- builder/azure/arm/artifact_test.go | 247 +--------------- builder/azure/arm/azure_client.go | 54 +--- builder/azure/arm/builder.go | 41 +-- builder/azure/arm/capture_template_test.go | 275 ------------------ builder/azure/arm/config.go | 4 + builder/azure/arm/step_capture_image.go | 97 +++--- builder/azure/arm/step_capture_image_test.go | 62 ++-- builder/azure/arm/step_deploy_template.go | 2 + builder/azure/arm/step_get_os_disk.go | 12 +- builder/azure/arm/step_get_os_disk_test.go | 6 +- builder/azure/arm/step_snapshot_os_disk.go | 2 +- .../azure/arm/step_snapshot_os_disk_test.go | 2 +- builder/azure/common/constants/stateBag.go | 3 +- 14 files changed, 161 insertions(+), 732 deletions(-) delete mode 100644 builder/azure/arm/capture_template_test.go diff --git a/builder/azure/arm/artifact.go b/builder/azure/arm/artifact.go index d022942e..4163d5dd 100644 --- a/builder/azure/arm/artifact.go +++ b/builder/azure/arm/artifact.go @@ -6,9 +6,6 @@ package arm import ( "bytes" "fmt" - "log" - "net/url" - "path" "strings" "github.com/hashicorp/packer-plugin-azure/builder/azure/common/constants" @@ -53,7 +50,7 @@ type Artifact struct { StateData map[string]interface{} } -func NewManagedImageArtifact(osType, resourceGroup, name, location, id, osDiskSnapshotName, dataDiskSnapshotPrefix string, generatedData map[string]interface{}, keepOSDisk bool, template *CaptureTemplate) (*Artifact, error) { +func NewManagedImageArtifact(osType, resourceGroup, name, location, id, osDiskSnapshotName, dataDiskSnapshotPrefix string, generatedData map[string]interface{}, osDiskUri string) (*Artifact, error) { res := Artifact{ ManagedImageResourceGroupName: resourceGroup, ManagedImageName: name, @@ -65,24 +62,8 @@ func NewManagedImageArtifact(osType, resourceGroup, name, location, id, osDiskSn 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() + if osDiskUri != "" { + res.OSDiskUri = osDiskUri } return &res, nil @@ -111,72 +92,33 @@ func NewSharedImageArtifact(osType, destinationSharedImageGalleryId string, loca }, nil } -func NewArtifact(template *CaptureTemplate, 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 - } +func NewArtifact(vmInternalID string, storageAccountUrl string, storageAccountLocation string, osType string, additionalDiskCount int, generatedData map[string]interface{}) (*Artifact, error) { + vhdUri := fmt.Sprintf("%ssystem/Microsoft.Compute/Images/images/packer-osDisk.%s.vhd", storageAccountUrl, vmInternalID) - templateUri, err := storageUriToTemplateUri(vhdUri) - if err != nil { - return nil, err - } + templateUri := fmt.Sprintf("%ssystem/Microsoft.Compute/Images/images/packer-vmTemplate.%s.json", storageAccountUrl, vmInternalID) 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() + if additionalDiskCount > 0 { + data_disks := make([]AdditionalDiskArtifact, additionalDiskCount) + for i := 0; i < additionalDiskCount; i++ { + data_disks[i].AdditionalDiskUri = fmt.Sprintf("%ssystem/Microsoft.Compute/Images/images/packer-datadisk-%d.%s.vhd", storageAccountUrl, i+1, vmInternalID) } additional_disks = &data_disks } return &Artifact{ OSType: osType, - OSDiskUri: vhdUri.String(), - TemplateUri: templateUri.String(), + OSDiskUri: vhdUri, + TemplateUri: templateUri, AdditionalDisks: additional_disks, - StorageAccountLocation: template.Resources[0].Location, + StorageAccountLocation: storageAccountLocation, StateData: generatedData, }, nil } -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 != "" } @@ -333,7 +275,7 @@ func (a *Artifact) hcpPackerRegistryMetadata() *registryimage.Image { return img } - // If image is a legacy VHD + // If image is a VHD labels["storage_account_location"] = a.StorageAccountLocation labels["template_uri"] = a.TemplateUri diff --git a/builder/azure/arm/artifact_test.go b/builder/azure/arm/artifact_test.go index e1582abe..8eb15d96 100644 --- a/builder/azure/arm/artifact_test.go +++ b/builder/azure/arm/artifact_test.go @@ -17,24 +17,7 @@ func generatedData() 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, "Linux", generatedData()) + artifact, err := NewArtifact("4085bb15-3644-4641-b9cd-f575918640b4", "https://storage.blob.core.windows.net/", "southcentralus", "Linux", 0, generatedData()) if err != nil { t.Fatalf("err=%s", err) } @@ -48,24 +31,7 @@ func TestArtifactIdVHD(t *testing.T) { } 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) + artifact, err := NewManagedImageArtifact("Linux", "fakeResourceGroup", "fakeName", "fakeLocation", "fakeID", "fakeOsDiskSnapshotName", "fakeDataDiskSnapshotPrefix", generatedData(), "") if err != nil { t.Fatalf("err=%s", err) } @@ -88,24 +54,7 @@ ManagedImageDataDiskSnapshotPrefix: fakeDataDiskSnapshotPrefix } 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) + artifact, err := NewManagedImageArtifact("Linux", "fakeResourceGroup", "fakeName", "fakeLocation", "fakeID", "", "fakeDataDiskSnapshotPrefix", generatedData(), "") if err != nil { t.Fatalf("err=%s", err) } @@ -127,24 +76,7 @@ ManagedImageDataDiskSnapshotPrefix: fakeDataDiskSnapshotPrefix } 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) + artifact, err := NewManagedImageArtifact("Linux", "fakeResourceGroup", "fakeName", "fakeLocation", "fakeID", "fakeOsDiskSnapshotName", "", generatedData(), "") if err != nil { t.Fatalf("err=%s", err) } @@ -166,24 +98,7 @@ ManagedImageOSDiskSnapshotName: fakeOsDiskSnapshotName } 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) + artifact, err := NewManagedImageArtifact("Linux", "fakeResourceGroup", "fakeName", "fakeLocation", "fakeID", "fakeOsDiskSnapshotName", "", generatedData(), "/subscriptions/subscription/resourceGroups/test/providers/Microsoft.Compute/images/myimage") if err != nil { t.Fatalf("err=%s", err) } @@ -196,7 +111,7 @@ 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 +OSDiskUri: /subscriptions/subscription/resourceGroups/test/providers/Microsoft.Compute/images/myimage ` result := artifact.String() @@ -391,24 +306,7 @@ SharedImageGalleryReplicatedRegions: fake-region-1, fake-region-2 } 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, "Linux", generatedData()) + artifact, err := NewArtifact("4085bb15-3644-4641-b9cd-f575918640b4", "https://storage.blob.core.windows.net/", "southcentralus", "Linux", 0, generatedData()) if err != nil { t.Fatalf("err=%s", err) } @@ -429,31 +327,7 @@ func TestArtifactString(t *testing.T) { } 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, "Linux", generatedData()) + artifact, err := NewArtifact("4085bb15-3644-4641-b9cd-f575918640b4", "https://storage.blob.core.windows.net/", "southcentralus", "Linux", 1, generatedData()) if err != nil { t.Fatalf("err=%s", err) } @@ -477,24 +351,7 @@ func TestAdditionalDiskArtifactString(t *testing.T) { } 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, "Linux", generatedData()) + testSubject, err := NewArtifact("4085bb15-3644-4641-b9cd-f575918640b4", "https://storage.blob.core.windows.net/", "southcentralus", "Linux", 0, generatedData()) if err != nil { t.Fatalf("err=%s", err) } @@ -514,31 +371,7 @@ func TestArtifactProperties(t *testing.T) { } 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, "Linux", generatedData()) + testSubject, err := NewArtifact("4085bb15-3644-4641-b9cd-f575918640b4", "https://storage.blob.core.windows.net/", "southcentralus", "Linux", 1, generatedData()) if err != nil { t.Fatalf("err=%s", err) } @@ -566,66 +399,6 @@ func TestAdditionalDiskArtifactProperties(t *testing.T) { } } -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, "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, "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, "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{ diff --git a/builder/azure/arm/azure_client.go b/builder/azure/arm/azure_client.go index f5833add..c1008ab0 100644 --- a/builder/azure/arm/azure_client.go +++ b/builder/azure/arm/azure_client.go @@ -5,7 +5,6 @@ package arm import ( "context" - "encoding/json" "fmt" "math" "os" @@ -58,59 +57,9 @@ type AzureClient struct { hashiGalleryImagesSDK.GalleryImagesClient GiovanniBlobClient giovanniBlobStorageSDK.Client InspectorMaxLength int - Template *CaptureTemplate LastError azureErrorResponse } -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 { @@ -174,7 +123,7 @@ func NewAzureClient(ctx context.Context, isVHDBuild bool, cloud *environments.En azureClient.VirtualMachinesClient = hashiVMSDK.NewVirtualMachinesClientWithBaseURI(*resourceManagerEndpoint) azureClient.VirtualMachinesClient.Client.Authorizer = authWrapper.AutorestAuthorizer(resourceManagerAuthorizer) azureClient.VirtualMachinesClient.Client.RequestInspector = withInspection(maxlen) - azureClient.VirtualMachinesClient.Client.ResponseInspector = byConcatDecorators(byInspecting(maxlen), templateCapture(azureClient), errorCapture(azureClient)) + azureClient.VirtualMachinesClient.Client.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient)) azureClient.VirtualMachinesClient.Client.UserAgent = fmt.Sprintf("%s %s", useragent.String(version.AzurePluginVersion.FormattedVersion()), azureClient.VirtualMachinesClient.Client.UserAgent) azureClient.VirtualMachinesClient.Client.PollingDuration = pollingDuration @@ -277,6 +226,7 @@ func NewAzureClient(ctx context.Context, isVHDBuild bool, cloud *environments.En if err != nil { return nil, nil, err } + // TODO Handle potential panic here if Access Token or child objects are null objectId, err := getObjectIdFromToken(token.AccessToken) if err != nil { return nil, nil, err diff --git a/builder/azure/arm/builder.go b/builder/azure/arm/builder.go index 83584324..8bd7c5ea 100644 --- a/builder/azure/arm/builder.go +++ b/builder/azure/arm/builder.go @@ -149,9 +149,6 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) 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 if !b.config.isPublishToSIG() { - // 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 != "" { @@ -172,7 +169,6 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) return nil, err } b.config.storageAccountBlobEndpoint = *account.Properties.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) } @@ -392,19 +388,10 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) return b.managedImageArtifactWithSIGAsDestination(managedImageID, stateData) } - 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, - stateData, - b.stateBag.Get(constants.ArmKeepOSDisk).(bool), - template.(*CaptureTemplate)) + var osDiskUri string + if b.stateBag.Get(constants.ArmKeepOSDisk).(bool) { + osDiskUri = b.stateBag.Get(constants.ArmOSDiskUri).(string) } - return NewManagedImageArtifact(b.config.OSType, b.config.ManagedImageResourceGroupName, b.config.ManagedImageName, @@ -413,22 +400,22 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) b.config.ManagedImageOSDiskSnapshotName, b.config.ManagedImageDataDiskSnapshotPrefix, stateData, - b.stateBag.Get(constants.ArmKeepOSDisk).(bool), - nil) + osDiskUri, + ) } if b.config.isPublishToSIG() { return b.sharedImageArtifact(stateData) } - - if template, ok := b.stateBag.GetOk(constants.ArmCaptureTemplate); ok { - return NewArtifact( - template.(*CaptureTemplate), - b.config.OSType, - stateData) - } - - return &Artifact{StateData: stateData}, nil + ui.Say(b.config.storageAccountBlobEndpoint) + ui.Say(fmt.Sprintf("%d", len(b.config.AdditionalDiskSize))) + return NewArtifact( + b.stateBag.Get(constants.ArmBuildVMInternalId).(string), + b.config.storageAccountBlobEndpoint, + b.config.StorageAccount, + b.config.OSType, + len(b.config.AdditionalDiskSize), + stateData) } func (b *Builder) writeSSHPrivateKey(ui packersdk.Ui, debugKeyPath string) { diff --git a/builder/azure/arm/capture_template_test.go b/builder/azure/arm/capture_template_test.go deleted file mode 100644 index 1b865c29..00000000 --- a/builder/azure/arm/capture_template_test.go +++ /dev/null @@ -1,275 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.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 index d19acbc6..a2980706 100644 --- a/builder/azure/arm/config.go +++ b/builder/azure/arm/config.go @@ -621,6 +621,10 @@ func (c *Config) getSourceSharedImageGalleryID() string { return id } +func (c *Config) getStorageAccountEndpoint() string { + return c.storageAccountBlobEndpoint +} + func (c *Config) toVMID() string { var resourceGroupName string if c.tmpResourceGroupName != "" { diff --git a/builder/azure/arm/step_capture_image.go b/builder/azure/arm/step_capture_image.go index 9681cba8..42cecaa2 100644 --- a/builder/azure/arm/step_capture_image.go +++ b/builder/azure/arm/step_capture_image.go @@ -5,6 +5,7 @@ package arm import ( "context" + "errors" "fmt" hashiImagesSDK "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-01/images" @@ -17,9 +18,9 @@ import ( type StepCaptureImage struct { client *AzureClient generalizeVM func(ctx context.Context, vmId hashiVMSDK.VirtualMachineId) error + getVMInternalID func(ctx context.Context, vmId hashiVMSDK.VirtualMachineId) (string, error) captureVhd func(ctx context.Context, vmId hashiVMSDK.VirtualMachineId, parameters *hashiVMSDK.VirtualMachineCaptureParameters) error captureManagedImage func(ctx context.Context, subscriptionId string, resourceGroupName string, imageName string, parameters *hashiImagesSDK.Image) error - get func(client *AzureClient) *CaptureTemplate say func(message string) error func(e error) } @@ -27,9 +28,6 @@ type StepCaptureImage struct { 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) }, @@ -41,7 +39,7 @@ func NewStepCaptureImage(client *AzureClient, ui packersdk.Ui) *StepCaptureImage step.generalizeVM = step.generalize step.captureVhd = step.captureImage step.captureManagedImage = step.captureImageFromVM - + step.getVMInternalID = step.getVMID return step } @@ -70,6 +68,18 @@ func (s *StepCaptureImage) captureImage(ctx context.Context, vmId hashiVMSDK.Vir return nil } +func (s *StepCaptureImage) getVMID(ctx context.Context, vmId hashiVMSDK.VirtualMachineId) (string, error) { + vmResponse, err := s.client.VirtualMachinesClient.Get(ctx, vmId, hashiVMSDK.DefaultGetOperationOptions()) + if err != nil { + return "", err + } + if vmResponse.Model != nil { + vmId := vmResponse.Model.Properties.VMId + return *vmId, nil + } + return "", nil +} + func (s *StepCaptureImage) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { var computeName = state.Get(constants.ArmComputeName).(string) @@ -92,45 +102,62 @@ func (s *StepCaptureImage) Run(ctx context.Context, state multistep.StateBag) mu } else { s.say("Generalizing machine ...") err := s.generalizeVM(ctx, vmId) - - if err == nil { - if isManagedImage { - s.say("Capturing image ...") - 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(" -> Image ResourceGroupName : '%s'", targetManagedImageResourceGroupName)) - s.say(fmt.Sprintf(" -> Image Name : '%s'", targetManagedImageName)) - s.say(fmt.Sprintf(" -> Image Location : '%s'", targetManagedImageLocation)) - err = s.captureManagedImage(ctx, subscriptionId, targetManagedImageResourceGroupName, targetManagedImageName, imageParameters) - } else if isSIGImage { - // It's possible to create SIG image - return multistep.ActionContinue - } else { - s.say("Capturing VHD ...") - err = s.captureVhd(ctx, vmId, 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) + if isManagedImage { + s.say("Capturing image ...") + 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(" -> Image ResourceGroupName : '%s'", targetManagedImageResourceGroupName)) + s.say(fmt.Sprintf(" -> Image Name : '%s'", targetManagedImageName)) + s.say(fmt.Sprintf(" -> Image Location : '%s'", targetManagedImageLocation)) + err = s.captureManagedImage(ctx, subscriptionId, targetManagedImageResourceGroupName, targetManagedImageName, imageParameters) + } else if isSIGImage { + // It's possible to create SIG image without a managed image + return multistep.ActionContinue + } else { + // VHD Builds are created with a field called the VMId in its name + // Get that ID before capturing the VM so that we know where the resultant VHD is stored + vmInternalID, err := s.getVMInternalID(ctx, vmId) + if err != nil { + err = fmt.Errorf("Failed to get build VM before capturing image with err : %s", err) + state.Put(constants.Error, err) + s.error(err) + return multistep.ActionHalt + } else { + if vmInternalID == "" { + err = errors.New("Failed to get build VM before capturing image, Azure did not return the field VirtualMachine.Properties.VMId") + state.Put(constants.Error, err) + s.error(err) + return multistep.ActionHalt + } else { + s.say(fmt.Sprintf(" -> VM Internal ID : '%s'", vmInternalID)) + state.Put(constants.ArmBuildVMInternalId, vmInternalID) + s.say("Capturing VHD ...") + err = s.captureVhd(ctx, vmId, vmCaptureParameters) + if err != nil { + state.Put(constants.Error, err) + s.error(err) + return multistep.ActionHalt + } + } + } + } } return multistep.ActionContinue } +func (s *StepCaptureImage) haltAndError(state multistep.StateBag, err error) multistep.StepAction { + state.Put(constants.Error, err) + s.error(err) + + return multistep.ActionHalt +} + 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 index 092a5647..86bb739c 100644 --- a/builder/azure/arm/step_capture_image_test.go +++ b/builder/azure/arm/step_capture_image_test.go @@ -19,10 +19,37 @@ func TestStepCaptureImageShouldFailIfCaptureFails(t *testing.T) { captureVhd: func(context.Context, hashiVMSDK.VirtualMachineId, *hashiVMSDK.VirtualMachineCaptureParameters) error { return fmt.Errorf("!! Unit Test FAIL !!") }, + getVMInternalID: func(context.Context, hashiVMSDK.VirtualMachineId) (string, error) { + return "id", nil + }, generalizeVM: func(context.Context, hashiVMSDK.VirtualMachineId) error { return nil }, - get: func(client *AzureClient) *CaptureTemplate { + 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 TestStepCaptureImageShouldFailIfGetVMIDFails(t *testing.T) { + var testSubject = &StepCaptureImage{ + captureVhd: func(context.Context, hashiVMSDK.VirtualMachineId, *hashiVMSDK.VirtualMachineCaptureParameters) error { + return nil + }, + getVMInternalID: func(context.Context, hashiVMSDK.VirtualMachineId) (string, error) { + return "", fmt.Errorf("!! Unit Test FAIL !!") + }, + generalizeVM: func(context.Context, hashiVMSDK.VirtualMachineId) error { return nil }, say: func(message string) {}, @@ -40,7 +67,6 @@ func TestStepCaptureImageShouldFailIfCaptureFails(t *testing.T) { 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(ctx context.Context, vmId hashiVMSDK.VirtualMachineId, parameters *hashiVMSDK.VirtualMachineCaptureParameters) error { @@ -49,15 +75,15 @@ func TestStepCaptureImageShouldPassIfCapturePasses(t *testing.T) { generalizeVM: func(context.Context, hashiVMSDK.VirtualMachineId) error { return nil }, - get: func(client *AzureClient) *CaptureTemplate { - return nil + getVMInternalID: func(ctx context.Context, vmId hashiVMSDK.VirtualMachineId) (string, error) { + return "id", nil }, say: func(message string) {}, error: func(e error) {}, } stateBag := createTestStateBagStepCaptureImage() - + stateBag.Put(constants.ArmIsSIGImage, true) var result = testSubject.Run(context.Background(), stateBag) if result != multistep.ActionContinue { t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result) @@ -78,14 +104,12 @@ func TestStepCaptureImageShouldCallGeneralizeIfSpecializedIsFalse(t *testing.T) generalizeCount++ return nil }, - get: func(client *AzureClient) *CaptureTemplate { - return nil - }, say: func(message string) {}, error: func(e error) {}, } stateBag := createTestStateBagStepCaptureImage() + stateBag.Put(constants.ArmIsSIGImage, true) stateBag.Put(constants.ArmSharedImageGalleryDestinationSpecialized, false) var result = testSubject.Run(context.Background(), stateBag) if result != multistep.ActionContinue { @@ -110,9 +134,6 @@ func TestStepCaptureImageShouldNotCallGeneralizeIfSpecializedIsTrue(t *testing.T generalizeCount++ return nil }, - get: func(client *AzureClient) *CaptureTemplate { - return nil - }, say: func(message string) {}, error: func(e error) {}, } @@ -139,10 +160,7 @@ func TestStepCaptureImageShouldTakeStepArgumentsFromStateBag(t *testing.T) { var actualResourceGroupName string var actualComputeName string var actualVirtualMachineCaptureParameters *hashiVMSDK.VirtualMachineCaptureParameters - actualCaptureTemplate := &CaptureTemplate{ - Schema: "!! Unit Test !!", - } - + expectedVirtualMachineID := "id" var testSubject = &StepCaptureImage{ captureVhd: func(ctx context.Context, id hashiVMSDK.VirtualMachineId, parameters *hashiVMSDK.VirtualMachineCaptureParameters) error { actualResourceGroupName = id.ResourceGroupName @@ -151,12 +169,12 @@ func TestStepCaptureImageShouldTakeStepArgumentsFromStateBag(t *testing.T) { return nil }, + getVMInternalID: func(ctx context.Context, vmId hashiVMSDK.VirtualMachineId) (string, error) { + return expectedVirtualMachineID, nil + }, generalizeVM: func(context.Context, hashiVMSDK.VirtualMachineId) error { return nil }, - get: func(client *AzureClient) *CaptureTemplate { - return actualCaptureTemplate - }, say: func(message string) {}, error: func(e error) {}, } @@ -171,8 +189,11 @@ func TestStepCaptureImageShouldTakeStepArgumentsFromStateBag(t *testing.T) { var expectedComputeName = stateBag.Get(constants.ArmComputeName).(string) var expectedResourceGroupName = stateBag.Get(constants.ArmResourceGroupName).(string) var expectedVirtualMachineCaptureParameters = stateBag.Get(constants.ArmNewVirtualMachineCaptureParameters).(*hashiVMSDK.VirtualMachineCaptureParameters) - var expectedCaptureTemplate = stateBag.Get(constants.ArmCaptureTemplate).(*CaptureTemplate) + actualVirtualMachineID := stateBag.Get(constants.ArmBuildVMInternalId).(string) + if actualVirtualMachineID != expectedVirtualMachineID { + t.Fatalf("Expected StepCaptureImage to set 'constants.ArmBuildVMInternalId' to the state bag to %s, but it was set to %s.", expectedVirtualMachineID, actualVirtualMachineID) + } if actualComputeName != expectedComputeName { t.Fatal("Expected StepCaptureImage to source 'constants.ArmComputeName' from the state bag, but it did not.") } @@ -185,9 +206,6 @@ func TestStepCaptureImageShouldTakeStepArgumentsFromStateBag(t *testing.T) { 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 { diff --git a/builder/azure/arm/step_deploy_template.go b/builder/azure/arm/step_deploy_template.go index 62424323..e7f5af23 100644 --- a/builder/azure/arm/step_deploy_template.go +++ b/builder/azure/arm/step_deploy_template.go @@ -133,6 +133,7 @@ func (s *StepDeployTemplate) Cleanup(state multistep.StateBag) { "Error: %s", imageName, err)) } } + ui.Say(fmt.Sprintf("Skipping deletion -> %s : '%s' since 'keep_os_disk' is set to true", imageType, imageName)) var dataDisks []string if disks := state.Get(constants.ArmAdditionalDiskVhds); disks != nil { @@ -180,6 +181,7 @@ func (s *StepDeployTemplate) deleteDeploymentObject(ctx context.Context, state m } func (s *StepDeployTemplate) getImageDetails(ctx context.Context, subscriptionId string, resourceGroupName string, computeName string) (string, string, error) { + //TODO is this still true? //We can't depend on constants.ArmOSDiskVhd being set var imageName, imageType string vmID := hashiVMSDK.NewVirtualMachineID(subscriptionId, resourceGroupName, computeName) diff --git a/builder/azure/arm/step_get_os_disk.go b/builder/azure/arm/step_get_os_disk.go index fed69479..676413e3 100644 --- a/builder/azure/arm/step_get_os_disk.go +++ b/builder/azure/arm/step_get_os_disk.go @@ -64,16 +64,16 @@ func (s *StepGetOSDisk) Run(ctx context.Context, state multistep.StateBag) multi return multistep.ActionHalt } - var vhdUri string + var diskUri string if vm.Properties.StorageProfile.OsDisk.Vhd != nil { - vhdUri = *vm.Properties.StorageProfile.OsDisk.Vhd.Uri - s.say(fmt.Sprintf(" -> OS Disk : '%s'", vhdUri)) + diskUri = *vm.Properties.StorageProfile.OsDisk.Vhd.Uri + s.say(fmt.Sprintf(" -> OS Disk : '%s'", diskUri)) } else { - vhdUri = *vm.Properties.StorageProfile.OsDisk.ManagedDisk.Id - s.say(fmt.Sprintf(" -> Managed OS Disk : '%s'", vhdUri)) + diskUri = *vm.Properties.StorageProfile.OsDisk.ManagedDisk.Id + s.say(fmt.Sprintf(" -> Managed OS Disk : '%s'", diskUri)) } - state.Put(constants.ArmOSDiskVhd, vhdUri) + state.Put(constants.ArmOSDiskUri, diskUri) return multistep.ActionContinue } diff --git a/builder/azure/arm/step_get_os_disk_test.go b/builder/azure/arm/step_get_os_disk_test.go index 4b25db12..8df9d20f 100644 --- a/builder/azure/arm/step_get_os_disk_test.go +++ b/builder/azure/arm/step_get_os_disk_test.go @@ -95,13 +95,13 @@ func TestStepGetOSDiskShouldTakeValidateArgumentsFromStateBag(t *testing.T) { t.Fatal("Expected the step to source 'constants.ArmResourceGroupName' from the state bag, but it did not.") } - expectedOSDiskVhd, ok := stateBag.GetOk(constants.ArmOSDiskVhd) + expectedOSDiskVhd, ok := stateBag.GetOk(constants.ArmOSDiskUri) if !ok { - t.Fatalf("Expected the state bag to have a value for '%s', but it did not.", constants.ArmOSDiskVhd) + t.Fatalf("Expected the state bag to have a value for '%s', but it did not.", constants.ArmOSDiskUri) } if expectedOSDiskVhd != "test.vhd" { - t.Fatalf("Expected the value of stateBag[%s] to be 'test.vhd', but got '%s'.", constants.ArmOSDiskVhd, expectedOSDiskVhd) + t.Fatalf("Expected the value of stateBag[%s] to be 'test.vhd', but got '%s'.", constants.ArmOSDiskUri, expectedOSDiskVhd) } } diff --git a/builder/azure/arm/step_snapshot_os_disk.go b/builder/azure/arm/step_snapshot_os_disk.go index 54678ac1..7e04eebb 100644 --- a/builder/azure/arm/step_snapshot_os_disk.go +++ b/builder/azure/arm/step_snapshot_os_disk.go @@ -74,7 +74,7 @@ func (s *StepSnapshotOSDisk) Run(ctx context.Context, stateBag multistep.StateBa var resourceGroupName = stateBag.Get(constants.ArmManagedImageResourceGroupName).(string) var location = stateBag.Get(constants.ArmLocation).(string) var tags = stateBag.Get(constants.ArmNewSDKTags).(map[string]string) - var srcUriVhd = stateBag.Get(constants.ArmOSDiskVhd).(string) + var srcUriVhd = stateBag.Get(constants.ArmOSDiskUri).(string) var dstSnapshotName = stateBag.Get(constants.ArmManagedImageOSDiskSnapshotName).(string) var subscriptionId = stateBag.Get(constants.ArmSubscription).(string) diff --git a/builder/azure/arm/step_snapshot_os_disk_test.go b/builder/azure/arm/step_snapshot_os_disk_test.go index 9bb279f5..5e7d4ab1 100644 --- a/builder/azure/arm/step_snapshot_os_disk_test.go +++ b/builder/azure/arm/step_snapshot_os_disk_test.go @@ -89,7 +89,7 @@ func createTestStateBagStepSnapshotOSDisk() multistep.StateBag { stateBag.Put(constants.ArmTags, tags) stateBag.Put(constants.ArmNewSDKTags, newSDKTags) - stateBag.Put(constants.ArmOSDiskVhd, "subscriptions/123-456-789/resourceGroups/existingresourcegroup/providers/Microsoft.Compute/disks/osdisk") + stateBag.Put(constants.ArmOSDiskUri, "subscriptions/123-456-789/resourceGroups/existingresourcegroup/providers/Microsoft.Compute/disks/osdisk") stateBag.Put(constants.ArmManagedImageOSDiskSnapshotName, "Unit Test: ManagedImageOSDiskSnapshotName") stateBag.Put(constants.ArmSubscription, "Unit Test: Subscription") diff --git a/builder/azure/common/constants/stateBag.go b/builder/azure/common/constants/stateBag.go index 42450469..ad257389 100644 --- a/builder/azure/common/constants/stateBag.go +++ b/builder/azure/common/constants/stateBag.go @@ -29,7 +29,7 @@ const ( ArmNicName string = "arm.NicName" ArmKeyVaultName string = "arm.KeyVaultName" ArmLocation string = "arm.Location" - ArmOSDiskVhd string = "arm.OSDiskVhd" + ArmOSDiskUri string = "arm.OSDiskUri" ArmAdditionalDiskVhds string = "arm.AdditionalDiskVhds" ArmPublicIPAddressName string = "arm.PublicIPAddressName" ArmResourceGroupName string = "arm.ResourceGroupName" @@ -69,4 +69,5 @@ const ( ArmKeepOSDisk string = "arm.KeepOSDisk" ArmBuildDiskEncryptionSetId string = "arm.ArmBuildDiskEncryptionSetId" ArmSubscription string = "arm.Subscription" + ArmBuildVMInternalId string = "arm.BuildVMInternalId" ) From dc5d2d82987e84b3ac4628458af7ceaa27d7bce8 Mon Sep 17 00:00:00 2001 From: Jenna Goldstrich Date: Tue, 27 Jun 2023 15:03:16 -0700 Subject: [PATCH 04/15] Fail fast: Check if a SIG Destination Image Version exists before trying to create it Implement Lucas's feedback Make generate --- builder/azure/arm/builder.go | 9 ++ builder/azure/arm/config.go | 4 - builder/azure/arm/config.hcl2spec.go | 105 +----------------- builder/azure/arm/step_capture_image.go | 41 +++---- .../common/client/Config-not-required.mdx | 2 +- 5 files changed, 27 insertions(+), 134 deletions(-) diff --git a/builder/azure/arm/builder.go b/builder/azure/arm/builder.go index 8bd7c5ea..64a9d79a 100644 --- a/builder/azure/arm/builder.go +++ b/builder/azure/arm/builder.go @@ -16,6 +16,7 @@ import ( hashiImagesSDK "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-01/images" hashiGalleryImagesSDK "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-03/galleryimages" + hashiGalleryImageVersionsSDK "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-03/galleryimageversions" hashiStorageAccountsSDK "github.com/hashicorp/go-azure-sdk/resource-manager/storage/2022-09-01/storageaccounts" "github.com/hashicorp/go-azure-helpers/resourcemanager/commonids" @@ -202,6 +203,14 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) if err != nil { return nil, fmt.Errorf("the Shared Gallery Image '%s' to which to publish the managed image version to does not exist in the resource group '%s' or does not contain managed image '%s'", b.config.SharedGalleryDestination.SigDestinationGalleryName, b.config.SharedGalleryDestination.SigDestinationResourceGroup, b.config.SharedGalleryDestination.SigDestinationImageName) } + + // Check if a Image Version already exists for our target destination + galleryImageVersionId := hashiGalleryImageVersionsSDK.NewImageVersionID(sigSubscriptionID, b.config.SharedGalleryDestination.SigDestinationResourceGroup, b.config.SharedGalleryDestination.SigDestinationGalleryName, b.config.SharedGalleryDestination.SigDestinationImageName, b.config.SharedGalleryDestination.SigDestinationImageVersion) + _, err := azureClient.GalleryImageVersionsClient.Get(ctx, galleryImageVersionId, hashiGalleryImageVersionsSDK.DefaultGetOperationOptions()) + if err == nil { + return nil, fmt.Errorf("a gallery image version for image name:version %s:%s already exists in gallery %s", b.config.SharedGalleryDestination.SigDestinationImageName, b.config.SharedGalleryDestination.SigDestinationImageVersion, b.config.SharedGalleryDestination.SigDestinationGalleryName) + } + // SIG requires that replication regions include the region in which the Managed Image resides managedImageLocation := normalizeAzureRegion(b.stateBag.Get(constants.ArmLocation).(string)) foundMandatoryReplicationRegion := false diff --git a/builder/azure/arm/config.go b/builder/azure/arm/config.go index a2980706..d19acbc6 100644 --- a/builder/azure/arm/config.go +++ b/builder/azure/arm/config.go @@ -621,10 +621,6 @@ func (c *Config) getSourceSharedImageGalleryID() string { return id } -func (c *Config) getStorageAccountEndpoint() string { - return c.storageAccountBlobEndpoint -} - func (c *Config) toVMID() string { var resourceGroupName string if c.tmpResourceGroupName != "" { diff --git a/builder/azure/arm/config.hcl2spec.go b/builder/azure/arm/config.hcl2spec.go index b6bf1fd2..091786a2 100644 --- a/builder/azure/arm/config.hcl2spec.go +++ b/builder/azure/arm/config.hcl2spec.go @@ -187,109 +187,6 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "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}, -<<<<<<< HEAD - "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}, - "spot": &hcldec.BlockSpec{TypeName: "spot", Nested: hcldec.ObjectSpec((*FlatSpot)(nil).HCL2Spec())}, - "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}, - "disk_encryption_set_id": &hcldec.AttrSpec{Name: "disk_encryption_set_id", 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}, - "custom_data": &hcldec.AttrSpec{Name: "custom_data", Type: cty.String, Required: false}, - "user_data_file": &hcldec.AttrSpec{Name: "user_data_file", Type: cty.String, Required: false}, - "user_data": &hcldec.AttrSpec{Name: "user_data", Type: cty.String, Required: false}, - "custom_script": &hcldec.AttrSpec{Name: "custom_script", 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}, - "license_type": &hcldec.AttrSpec{Name: "license_type", Type: cty.String, Required: false}, - "secure_boot_enabled": &hcldec.AttrSpec{Name: "secure_boot_enabled", Type: cty.Bool, Required: false}, - "encryption_at_host": &hcldec.AttrSpec{Name: "encryption_at_host", Type: cty.Bool, Required: false}, - "vtpm_enabled": &hcldec.AttrSpec{Name: "vtpm_enabled", Type: cty.Bool, 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}, -======= "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}, @@ -339,6 +236,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "custom_resource_build_prefix": &hcldec.AttrSpec{Name: "custom_resource_build_prefix", Type: cty.String, Required: false}, "license_type": &hcldec.AttrSpec{Name: "license_type", Type: cty.String, Required: false}, "secure_boot_enabled": &hcldec.AttrSpec{Name: "secure_boot_enabled", Type: cty.Bool, Required: false}, + "encryption_at_host": &hcldec.AttrSpec{Name: "encryption_at_host", Type: cty.Bool, Required: false}, "vtpm_enabled": &hcldec.AttrSpec{Name: "vtpm_enabled", Type: cty.Bool, 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}, @@ -390,7 +288,6 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "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}, ->>>>>>> 04a4a81 (Switch AuthType back to private variable and use helper function to get it, switch from MeClient to ServicePrincipals client to get objectID) } return s } diff --git a/builder/azure/arm/step_capture_image.go b/builder/azure/arm/step_capture_image.go index 42cecaa2..b04565e3 100644 --- a/builder/azure/arm/step_capture_image.go +++ b/builder/azure/arm/step_capture_image.go @@ -5,7 +5,6 @@ package arm import ( "context" - "errors" "fmt" hashiImagesSDK "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-01/images" @@ -117,6 +116,12 @@ func (s *StepCaptureImage) Run(ctx context.Context, state multistep.StateBag) mu s.say(fmt.Sprintf(" -> Image Name : '%s'", targetManagedImageName)) s.say(fmt.Sprintf(" -> Image Location : '%s'", targetManagedImageLocation)) err = s.captureManagedImage(ctx, subscriptionId, targetManagedImageResourceGroupName, targetManagedImageName, imageParameters) + if err != nil { + state.Put(constants.Error, err) + s.error(err) + + return multistep.ActionHalt + } } else if isSIGImage { // It's possible to create SIG image without a managed image return multistep.ActionContinue @@ -129,35 +134,21 @@ func (s *StepCaptureImage) Run(ctx context.Context, state multistep.StateBag) mu state.Put(constants.Error, err) s.error(err) return multistep.ActionHalt - } else { - if vmInternalID == "" { - err = errors.New("Failed to get build VM before capturing image, Azure did not return the field VirtualMachine.Properties.VMId") - state.Put(constants.Error, err) - s.error(err) - return multistep.ActionHalt - } else { - s.say(fmt.Sprintf(" -> VM Internal ID : '%s'", vmInternalID)) - state.Put(constants.ArmBuildVMInternalId, vmInternalID) - s.say("Capturing VHD ...") - err = s.captureVhd(ctx, vmId, vmCaptureParameters) - if err != nil { - state.Put(constants.Error, err) - s.error(err) - return multistep.ActionHalt - } - } + } + + s.say(fmt.Sprintf(" -> VM Internal ID : '%s'", vmInternalID)) + state.Put(constants.ArmBuildVMInternalId, vmInternalID) + s.say("Capturing VHD ...") + err = s.captureVhd(ctx, vmId, vmCaptureParameters) + if err != nil { + state.Put(constants.Error, err) + s.error(err) + return multistep.ActionHalt } } } return multistep.ActionContinue } -func (s *StepCaptureImage) haltAndError(state multistep.StateBag, err error) multistep.StepAction { - state.Put(constants.Error, err) - s.error(err) - - return multistep.ActionHalt -} - func (*StepCaptureImage) Cleanup(multistep.StateBag) { } diff --git a/docs-partials/builder/azure/common/client/Config-not-required.mdx b/docs-partials/builder/azure/common/client/Config-not-required.mdx index fdaf31ff..36f0b1b4 100644 --- a/docs-partials/builder/azure/common/client/Config-not-required.mdx +++ b/docs-partials/builder/azure/common/client/Config-not-required.mdx @@ -1,6 +1,6 @@ -- `cloud_environment_name` (string) - One of Public, China, Germany, or +- `cloud_environment_name` (string) - One of Public, China, or USGovernment. Defaults to Public. Long forms such as USGovernmentCloud and AzureUSGovernmentCloud are also supported. From 2d3fdc6eba8e8a025e2090bc82308c613785694d Mon Sep 17 00:00:00 2001 From: Jenna Goldstrich Date: Thu, 6 Jul 2023 13:11:42 -0700 Subject: [PATCH 05/15] Port DTL Builder to new SDK ======= DTL almost done! Just need to get VMID instead of capture template method and start testing Port DTL provisioner to new SDK Make generate Accidently commited playing around with this field, but was missing comma Appease the lint lord DTL never supported VHDs, it just had all the code of it, so I didn't need to port some of this Use retry config instead of an infinite loop Delete more unused code Update comments Address linter --- builder/azure/arm/azure_client.go | 10 +- builder/azure/arm/capture_template.go | 87 ----- builder/azure/arm/step_deploy_template.go | 1 - .../step_publish_to_shared_image_gallery.go | 2 +- builder/azure/common/constants/stateBag.go | 2 +- builder/azure/dtl/artifact.go | 150 ++------- builder/azure/dtl/azure_client.go | 314 ++++++++++-------- builder/azure/dtl/builder.go | 114 ++++--- builder/azure/dtl/builder_acc_test.go | 4 +- builder/azure/dtl/config.go | 10 +- builder/azure/dtl/config.hcl2spec.go | 2 +- builder/azure/dtl/config_test.go | 8 +- builder/azure/dtl/resource_resolver.go | 101 +----- builder/azure/dtl/step_capture_image.go | 61 ++-- .../azure/dtl/step_delete_virtual_machine.go | 16 +- builder/azure/dtl/step_deploy_template.go | 187 ++++------- builder/azure/dtl/step_power_off_compute.go | 7 +- .../step_publish_to_shared_image_gallery.go | 55 ++- builder/azure/dtl/template_factory.go | 49 ++- .../builder/azure/dtl/Config-not-required.mdx | 2 +- go.mod | 1 - go.sum | 3 - provisioner/azure-dtlartifact/provisioner.go | 43 +-- 23 files changed, 447 insertions(+), 782 deletions(-) delete mode 100644 builder/azure/arm/capture_template.go diff --git a/builder/azure/arm/azure_client.go b/builder/azure/arm/azure_client.go index c1008ab0..70e735dd 100644 --- a/builder/azure/arm/azure_client.go +++ b/builder/azure/arm/azure_client.go @@ -176,7 +176,6 @@ func NewAzureClient(ctx context.Context, isVHDBuild bool, cloud *environments.En azureClient.ImagesClient.Client.UserAgent = fmt.Sprintf("%s %s", useragent.String(version.AzurePluginVersion.FormattedVersion()), azureClient.ImagesClient.Client.UserAgent) azureClient.ImagesClient.Client.PollingDuration = pollingDuration - // Clients that are using the existing SDK/auth logic azureClient.StorageAccountsClient = hashiStorageAccountsSDK.NewStorageAccountsClientWithBaseURI(*resourceManagerEndpoint) azureClient.StorageAccountsClient.Client.Authorizer = authWrapper.AutorestAuthorizer(resourceManagerAuthorizer) azureClient.StorageAccountsClient.Client.RequestInspector = withInspection(maxlen) @@ -184,11 +183,12 @@ func NewAzureClient(ctx context.Context, isVHDBuild bool, cloud *environments.En azureClient.StorageAccountsClient.Client.UserAgent = fmt.Sprintf("%s %s", useragent.String(version.AzurePluginVersion.FormattedVersion()), azureClient.StorageAccountsClient.Client.UserAgent) azureClient.StorageAccountsClient.Client.PollingDuration = pollingDuration - api := environments.AzurePublic().ResourceManager - networkMetaClient, err := hashiNetworkMetaSDK.NewClientWithBaseURI(api, func(c *resourcemanager.Client) { + // TODO Request/Response inpectors for Track 2 + networkMetaClient, err := hashiNetworkMetaSDK.NewClientWithBaseURI(cloud.ResourceManager, func(c *resourcemanager.Client) { c.Client.Authorizer = resourceManagerAuthorizer c.Client.UserAgent = "some-user-agent" }) + if err != nil { return nil, nil, err } @@ -281,7 +281,7 @@ func buildAuthorizer(ctx context.Context, authOpts NewSDKAuthOptions, env enviro var authConfig auth.Credentials switch authOpts.AuthType { case AuthTypeDeviceLogin: - return nil, fmt.Errorf("DeviceLogin is not supported, however you can use the Azure CLI `az login --use-device-code` to use a device code, and then use CLI authentication") + return nil, fmt.Errorf("DeviceLogin is not supported in v2 of the Azure Packer Plugin, however you can use the Azure CLI `az login --use-device-code` to use a device code, and then use CLI authentication") case AuthTypeAzureCLI: authConfig = auth.Credentials{ Environment: env, @@ -322,7 +322,6 @@ func buildAuthorizer(ctx context.Context, authOpts NewSDKAuthOptions, env enviro authorizer, err := auth.NewAuthorizerFromCredentials(ctx, authConfig, api) if err != nil { return nil, err - //fmt.Errorf("building Resource Manager authorizer from credentials: %+v", err) } return authorizer, nil } @@ -339,5 +338,4 @@ func getObjectIdFromToken(token string) (string, error) { return "", err } return claims["oid"].(string), nil - } diff --git a/builder/azure/arm/capture_template.go b/builder/azure/arm/capture_template.go deleted file mode 100644 index 85375513..00000000 --- a/builder/azure/arm/capture_template.go +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.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/step_deploy_template.go b/builder/azure/arm/step_deploy_template.go index e7f5af23..33efe269 100644 --- a/builder/azure/arm/step_deploy_template.go +++ b/builder/azure/arm/step_deploy_template.go @@ -216,7 +216,6 @@ func deleteResource(ctx context.Context, client *AzureClient, subscriptionId str switch resourceType { case "Microsoft.Compute/virtualMachines": vmID := hashiVMSDK.NewVirtualMachineID(subscriptionId, resourceGroupName, resourceName) - // TODO don't rely on default operations, set hard delete to false if err := client.VirtualMachinesClient.DeleteThenPoll(ctx, vmID, hashiVMSDK.DefaultDeleteOperationOptions()); err != nil { return err } diff --git a/builder/azure/arm/step_publish_to_shared_image_gallery.go b/builder/azure/arm/step_publish_to_shared_image_gallery.go index 6cf3adeb..b452bb77 100644 --- a/builder/azure/arm/step_publish_to_shared_image_gallery.go +++ b/builder/azure/arm/step_publish_to_shared_image_gallery.go @@ -45,7 +45,7 @@ func getSigDestinationStorageAccountType(s string) (hashiGalleryImageVersionsSDK return hashiGalleryImageVersionsSDK.StorageAccountTypeStandardLRS, nil } for _, t := range hashiGalleryImageVersionsSDK.PossibleValuesForStorageAccountType() { - if string(t) == s { + if s == t { return hashiGalleryImageVersionsSDK.StorageAccountType(t), nil } } diff --git a/builder/azure/common/constants/stateBag.go b/builder/azure/common/constants/stateBag.go index ad257389..20125ae7 100644 --- a/builder/azure/common/constants/stateBag.go +++ b/builder/azure/common/constants/stateBag.go @@ -20,7 +20,6 @@ const ( ) const ( - ArmCaptureTemplate string = "arm.CaptureTemplate" ArmComputeName string = "arm.ComputeName" ArmImageParameters string = "arm.ImageParameters" ArmCertificateUrl string = "arm.CertificateUrl" @@ -70,4 +69,5 @@ const ( ArmBuildDiskEncryptionSetId string = "arm.ArmBuildDiskEncryptionSetId" ArmSubscription string = "arm.Subscription" ArmBuildVMInternalId string = "arm.BuildVMInternalId" + DtlLabName string = "dtl.LabName" ) diff --git a/builder/azure/dtl/artifact.go b/builder/azure/dtl/artifact.go index 7b0feabf..a336b9e8 100644 --- a/builder/azure/dtl/artifact.go +++ b/builder/azure/dtl/artifact.go @@ -6,9 +6,6 @@ package dtl import ( "bytes" "fmt" - "net/url" - "path" - "strings" ) const ( @@ -24,20 +21,16 @@ 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 + ManagedImageResourceGroupName string + ManagedImageName string + ManagedImageLocation string + ManagedImageId string + + // Shared Image Gallery + // ARM resource id for Shared Image Gallery + ManagedImageSharedImageGalleryId string + SharedImageGalleryLocation string // Additional Disks AdditionalDisks *[]AdditionalDiskArtifact @@ -53,82 +46,17 @@ func NewManagedImageArtifact(osType, resourceGroup, name, location, id string) ( }, 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 - } - +func NewManagedImageArtifactWithSIGAsDestination(osType, resourceGroup, name, location, id, destinationSharedImageGalleryId string) (*Artifact, error) { 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, + ManagedImageResourceGroupName: resourceGroup, + ManagedImageName: name, + ManagedImageLocation: location, + ManagedImageId: id, + OSType: osType, + ManagedImageSharedImageGalleryId: destinationSharedImageGalleryId, }, 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 } @@ -138,16 +66,11 @@ func (*Artifact) Files() []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 } @@ -158,45 +81,16 @@ func (a *Artifact) String() string { 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)) - } - } + 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.ManagedImageSharedImageGalleryId != "" { + buf.WriteString(fmt.Sprintf("ManagedImageSharedImageGalleryId: %s\n", a.ManagedImageSharedImageGalleryId)) } - 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 index ba4fe7b6..31460f34 100644 --- a/builder/azure/dtl/azure_client.go +++ b/builder/azure/dtl/azure_client.go @@ -4,7 +4,7 @@ package dtl import ( - "encoding/json" + "context" "fmt" "math" "net/http" @@ -12,17 +12,20 @@ import ( "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/golang-jwt/jwt" + "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-azure/builder/azure/common" + hashiImagesSDK "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-01/images" + hashiVMSDK "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-01/virtualmachines" + hashiGalleryImagesSDK "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-03/galleryimages" + hashiGalleryImageVersionsSDK "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-03/galleryimageversions" + hashiDTLSDK "github.com/hashicorp/go-azure-sdk/resource-manager/devtestlab/2018-09-15" + hashiVaultsSDK "github.com/hashicorp/go-azure-sdk/resource-manager/keyvault/2023-02-01/vaults" + hashiNetworkSDK "github.com/hashicorp/go-azure-sdk/resource-manager/network/2022-09-01" + "github.com/hashicorp/go-azure-sdk/sdk/auth" + authWrapper "github.com/hashicorp/go-azure-sdk/sdk/auth/autorest" + "github.com/hashicorp/go-azure-sdk/sdk/client/resourcemanager" + "github.com/hashicorp/go-azure-sdk/sdk/environments" "github.com/hashicorp/packer-plugin-azure/version" "github.com/hashicorp/packer-plugin-sdk/useragent" ) @@ -32,81 +35,16 @@ const ( ) 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) - }) - } + InspectorMaxLength int + LastError azureErrorResponse + + hashiVMSDK.VirtualMachinesClient + hashiImagesSDK.ImagesClient + hashiVaultsSDK.VaultsClient + NetworkMetaClient hashiNetworkSDK.Client + hashiGalleryImageVersionsSDK.GalleryImageVersionsClient + hashiGalleryImagesSDK.GalleryImagesClient + DtlMetaClient hashiDTLSDK.Client } func errorCapture(client *AzureClient) autorest.RespondDecorator { @@ -134,74 +72,162 @@ func byConcatDecorators(decorators ...autorest.RespondDecorator) autorest.Respon } } -func NewAzureClient(subscriptionID, resourceGroupName string, - cloud *azure.Environment, SharedGalleryTimeout time.Duration, CustomImageCaptureTimeout time.Duration, PollingDuration time.Duration, - servicePrincipalToken *adal.ServicePrincipalToken) (*AzureClient, error) { +type NewSDKAuthOptions struct { + AuthType string + ClientID string + ClientSecret string + ClientJWT string + ClientCertPath string + TenantID string + SubscriptionID string +} + +// Returns an Azure Client used for the Azure Resource Manager +// Also returns the Azure object ID for the authentication method used in the build +func NewAzureClient(ctx context.Context, subscriptionID string, + cloud *environments.Environment, SharedGalleryTimeout time.Duration, CustomImageCaptureTimeout time.Duration, PollingDuration time.Duration, newSdkAuthOptions NewSDKAuthOptions) (*AzureClient, *string, 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 = CustomImageCaptureTimeout - - 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) + if cloud == nil || cloud.ResourceManager == nil { + // TODO Throw error message that helps users solve this problem + return nil, nil, fmt.Errorf("Azure Environment not configured correctly") + } + resourceManagerEndpoint, _ := cloud.ResourceManager.Endpoint() + resourceManagerAuthorizer, err := buildResourceManagerAuthorizer(ctx, newSdkAuthOptions, *cloud) + if err != nil { + return nil, nil, err + } + dtlMetaClient := hashiDTLSDK.NewClientWithBaseURI(*resourceManagerEndpoint, func(c *autorest.Client) { + c.Authorizer = authWrapper.AutorestAuthorizer(resourceManagerAuthorizer) + c.UserAgent = fmt.Sprintf("%s %s", useragent.String(version.AzurePluginVersion.FormattedVersion()), "go-azure-sdk Meta Client") + c.RequestInspector = withInspection(maxlen) + c.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient)) + }) + azureClient.DtlMetaClient = dtlMetaClient + + azureClient.GalleryImageVersionsClient = hashiGalleryImageVersionsSDK.NewGalleryImageVersionsClientWithBaseURI(*resourceManagerEndpoint) + azureClient.GalleryImageVersionsClient.Client.Authorizer = authWrapper.AutorestAuthorizer(resourceManagerAuthorizer) + azureClient.GalleryImageVersionsClient.Client.RequestInspector = withInspection(maxlen) + azureClient.GalleryImageVersionsClient.Client.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient)) + azureClient.GalleryImageVersionsClient.Client.UserAgent = fmt.Sprintf("%s %s", useragent.String(version.AzurePluginVersion.FormattedVersion()), azureClient.GalleryImageVersionsClient.Client.UserAgent) + azureClient.GalleryImageVersionsClient.Client.PollingDuration = PollingDuration + + azureClient.GalleryImagesClient = hashiGalleryImagesSDK.NewGalleryImagesClientWithBaseURI(*resourceManagerEndpoint) + azureClient.GalleryImagesClient.Client.Authorizer = authWrapper.AutorestAuthorizer(resourceManagerAuthorizer) + azureClient.GalleryImagesClient.Client.RequestInspector = withInspection(maxlen) + azureClient.GalleryImagesClient.Client.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient)) + azureClient.GalleryImagesClient.Client.UserAgent = fmt.Sprintf("%s %s", useragent.String(version.AzurePluginVersion.FormattedVersion()), azureClient.GalleryImagesClient.Client.UserAgent) azureClient.GalleryImagesClient.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.ImagesClient = hashiImagesSDK.NewImagesClientWithBaseURI(*resourceManagerEndpoint) + azureClient.ImagesClient.Client.Authorizer = authWrapper.AutorestAuthorizer(resourceManagerAuthorizer) + azureClient.ImagesClient.Client.RequestInspector = withInspection(maxlen) + azureClient.ImagesClient.Client.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient)) + azureClient.ImagesClient.Client.UserAgent = fmt.Sprintf("%s %s", useragent.String(version.AzurePluginVersion.FormattedVersion()), azureClient.ImagesClient.Client.UserAgent) + azureClient.ImagesClient.Client.PollingDuration = PollingDuration - return azureClient, nil + // TODO Request/Response inpectors for Track 2 + networkMetaClient, err := hashiNetworkSDK.NewClientWithBaseURI(cloud.ResourceManager, func(c *resourcemanager.Client) { + c.Client.Authorizer = resourceManagerAuthorizer + c.Client.UserAgent = "some-user-agent" + }) + + if err != nil { + return nil, nil, err + } + azureClient.NetworkMetaClient = *networkMetaClient + token, err := resourceManagerAuthorizer.Token(ctx, &http.Request{}) + if err != nil { + return nil, nil, err + } + objectId, err := getObjectIdFromToken(token.AccessToken) + if err != nil { + return nil, nil, err + } + return azureClient, &objectId, nil +} + +const ( + AuthTypeDeviceLogin = "DeviceLogin" + AuthTypeMSI = "ManagedIdentity" + AuthTypeClientSecret = "ClientSecret" + AuthTypeClientCert = "ClientCertificate" + AuthTypeClientBearerJWT = "ClientBearerJWT" + AuthTypeAzureCLI = "AzureCLI" +) + +func buildResourceManagerAuthorizer(ctx context.Context, authOpts NewSDKAuthOptions, env environments.Environment) (auth.Authorizer, error) { + authorizer, err := buildAuthorizer(ctx, authOpts, env, env.ResourceManager) + if err != nil { + return nil, fmt.Errorf("building Resource Manager authorizer from credentials: %+v", err) + } + return authorizer, nil +} + +func buildAuthorizer(ctx context.Context, authOpts NewSDKAuthOptions, env environments.Environment, api environments.Api) (auth.Authorizer, error) { + var authConfig auth.Credentials + switch authOpts.AuthType { + case AuthTypeDeviceLogin: + return nil, fmt.Errorf("DeviceLogin is not supported in v2 of the Azure Packer Plugin, however you can use the Azure CLI `az login --use-device-code` to use a device code, and then use CLI authentication") + case AuthTypeAzureCLI: + authConfig = auth.Credentials{ + Environment: env, + EnableAuthenticatingUsingAzureCLI: true, + } + case AuthTypeMSI: + authConfig = auth.Credentials{ + Environment: env, + EnableAuthenticatingUsingManagedIdentity: true, + } + case AuthTypeClientSecret: + authConfig = auth.Credentials{ + Environment: env, + EnableAuthenticatingUsingClientSecret: true, + ClientID: authOpts.ClientID, + ClientSecret: authOpts.ClientSecret, + TenantID: authOpts.TenantID, + } + case AuthTypeClientCert: + authConfig = auth.Credentials{ + Environment: env, + EnableAuthenticatingUsingClientCertificate: true, + ClientID: authOpts.ClientID, + ClientCertificatePath: authOpts.ClientCertPath, + ClientCertificatePassword: "", + } + case AuthTypeClientBearerJWT: + authConfig = auth.Credentials{ + Environment: env, + EnableAuthenticationUsingOIDC: true, + ClientID: authOpts.ClientID, + TenantID: authOpts.TenantID, + OIDCAssertionToken: authOpts.ClientJWT, + } + default: + panic("AuthType not set") + } + authorizer, err := auth.NewAuthorizerFromCredentials(ctx, authConfig, api) + if err != nil { + return nil, err + } + return authorizer, nil } +func getObjectIdFromToken(token string) (string, error) { + claims := jwt.MapClaims{} + var p jwt.Parser + + var err error + + _, _, err = p.ParseUnverified(token, claims) + if err != nil { + return "", err + } + return claims["oid"].(string), nil +} func getInspectorMaxLength() int64 { value, ok := os.LookupEnv(EnvPackerLogAzureMaxLen) if !ok { diff --git a/builder/azure/dtl/builder.go b/builder/azure/dtl/builder.go index e00301fe..1d7096f7 100644 --- a/builder/azure/dtl/builder.go +++ b/builder/azure/dtl/builder.go @@ -12,10 +12,10 @@ import ( "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/golang-jwt/jwt" + hashiGalleryImagesSDK "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-03/galleryimages" + hashiDTLCustomImagesSDK "github.com/hashicorp/go-azure-sdk/resource-manager/devtestlab/2018-09-15/customimages" + hashiDTLLabsSDK "github.com/hashicorp/go-azure-sdk/resource-manager/devtestlab/2018-09-15/labs" + hashiDTLVNETSDK "github.com/hashicorp/go-azure-sdk/resource-manager/devtestlab/2018-09-15/virtualnetworks" "github.com/hashicorp/hcl/v2/hcldec" packerAzureCommon "github.com/hashicorp/packer-plugin-azure/builder/azure/common" "github.com/hashicorp/packer-plugin-azure/builder/azure/common/constants" @@ -32,6 +32,8 @@ type Builder struct { runner multistep.Runner } +var ErrNoImage = errors.New("failed to find shared image gallery id in state") + const ( DefaultSasBlobContainer = "system/Microsoft.Compute" DefaultSecretName = "packerKeyVaultSecret" @@ -71,20 +73,25 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) b.stateBag.Put("hook", hook) b.stateBag.Put(constants.Ui, ui) - spnCloud, err := b.getServicePrincipalToken(ui.Say) - if err != nil { - return nil, err + // Pass in relevant auth information for hashicorp/go-azure-sdk + authOptions := NewSDKAuthOptions{ + AuthType: b.config.ClientConfig.AuthType(), + ClientID: b.config.ClientConfig.ClientID, + ClientSecret: b.config.ClientConfig.ClientSecret, + ClientJWT: b.config.ClientConfig.ClientJWT, + ClientCertPath: b.config.ClientConfig.ClientCertPath, + TenantID: b.config.ClientConfig.TenantID, + SubscriptionID: b.config.ClientConfig.SubscriptionID, } - ui.Message("Creating Azure DevTestLab (DTL) client ...") - azureClient, err := NewAzureClient( + azureClient, objectId, err := NewAzureClient( + ctx, b.config.ClientConfig.SubscriptionID, - b.config.LabResourceGroupName, - b.config.ClientConfig.CloudEnvironment(), + b.config.ClientConfig.NewCloudEnvironment(), b.config.SharedGalleryTimeout, b.config.CustomImageCaptureTimeout, b.config.PollingDurationTimeout, - spnCloud) + authOptions) if err != nil { return nil, err @@ -95,7 +102,7 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) return nil, err } if b.config.ClientConfig.ObjectID == "" { - b.config.ClientConfig.ObjectID = getObjectIdFromToken(ui, spnCloud) + b.config.ClientConfig.ObjectID = *objectId } else { ui.Message("You have provided Object_ID which is no longer needed, azure packer builder determines this dynamically from the authentication token") } @@ -106,15 +113,13 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) 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, "") + customImageResourceId := hashiDTLCustomImagesSDK.NewCustomImageID(b.config.ClientConfig.SubscriptionID, b.config.ManagedImageResourceGroupName, b.config.LabName, b.config.ManagedImageName) + _, err = azureClient.DtlMetaClient.CustomImages.Get(ctx, customImageResourceId, hashiDTLCustomImagesSDK.DefaultGetOperationOptions()) 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) - } + err := azureClient.DtlMetaClient.CustomImages.DeleteThenPoll(ctx, customImageResourceId) if err != nil { return nil, fmt.Errorf("failed to delete the managed image named %s : %s", b.config.ManagedImageName, azureClient.LastError.Error()) } @@ -136,12 +141,16 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) deploymentName := b.stateBag.Get(constants.ArmDeploymentName).(string) + b.stateBag.Put(constants.DtlLabName, b.config.LabName) // 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) + sigSubscriptionID := b.stateBag.Get(constants.ArmSubscription).(string) + galleryId := hashiGalleryImagesSDK.NewGalleryImageID(sigSubscriptionID, b.config.SharedGalleryDestination.SigDestinationResourceGroup, b.config.SharedGalleryDestination.SigDestinationGalleryName, b.config.SharedGalleryDestination.SigDestinationImageName) + _, err = azureClient.GalleryImagesClient.Get(ctx, galleryId) 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) + return nil, fmt.Errorf("the Shared Gallery Image '%s' to which to publish the managed image version to does not exist in the resource group '%s' or does not contain managed image '%s'", b.config.SharedGalleryDestination.SigDestinationGalleryName, b.config.SharedGalleryDestination.SigDestinationResourceGroup, b.config.SharedGalleryDestination.SigDestinationImageName) } + // SIG requires that replication regions include the region in which the Managed Image resides managedImageLocation := normalizeAzureRegion(b.stateBag.Get(constants.ArmLocation).(string)) foundMandatoryReplicationRegion := false @@ -162,12 +171,12 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) } // Find the lab location - lab, err := azureClient.DtlLabsClient.Get(ctx, b.config.LabResourceGroupName, b.config.LabName, "") + labResourceId := hashiDTLLabsSDK.NewLabID(b.config.ClientConfig.SubscriptionID, b.config.LabResourceGroupName, b.config.LabName) + lab, err := azureClient.DtlMetaClient.Labs.Get(ctx, labResourceId, hashiDTLLabsSDK.DefaultGetOperationOptions()) 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 + b.config.Location = *lab.Model.Location if b.config.LabVirtualNetworkName == "" || b.config.LabSubnetName == "" { virtualNetwork, subnet, err := b.getSubnetInformation(ctx, ui, *azureClient) @@ -259,11 +268,11 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) 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) + 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.isPublishToSIG() { + return b.managedImageArtifactWithSIGAsDestination(managedImageID) } - return &Artifact{}, nil + return NewManagedImageArtifact(b.config.OSType, b.config.ManagedImageResourceGroupName, b.config.ManagedImageName, b.config.Location, managedImageID) } func (b *Builder) writeSSHPrivateKey(ui packersdk.Ui, debugKeyPath string) { @@ -290,6 +299,8 @@ func (b *Builder) writeSSHPrivateKey(ui packersdk.Ui, debugKeyPath string) { 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.ArmNewSDKTags, b.config.AzureTags) stateBag.Put(constants.ArmTags, b.config.AzureTags) stateBag.Put(constants.ArmComputeName, b.config.tmpComputeName) stateBag.Put(constants.ArmDeploymentName, b.config.tmpDeploymentName) @@ -313,6 +324,7 @@ func (b *Builder) configureStateBag(stateBag multistep.StateBag) { stateBag.Put(constants.ArmManagedImageSharedGalleryImageVersion, b.config.SharedGalleryDestination.SigDestinationImageVersion) stateBag.Put(constants.ArmManagedImageSubscription, b.config.ClientConfig.SubscriptionID) } + stateBag.Put(constants.ArmSubscription, b.config.ClientConfig.SubscriptionID) } // Parameters that are only known at runtime after querying Azure. @@ -324,23 +336,21 @@ 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, "") + num := int64(10) + labResourceId := hashiDTLVNETSDK.NewLabID(b.config.ClientConfig.SubscriptionID, b.config.LabResourceGroupName, b.config.LabName) + virtualNetworkPage, err := azClient.DtlMetaClient.VirtualNetworks.List(ctx, labResourceId, hashiDTLVNETSDK.ListOperationOptions{Top: &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 { + virtualNetworks := virtualNetworkPage.Model + for _, virtualNetwork := range *virtualNetworks { + for _, subnetOverride := range *virtualNetwork.Properties.SubnetOverrides { + // Check if the Subnet is allowed to create VMs having Public IP - if subnetOverride.UseInVMCreationPermission == dtl.Allow && subnetOverride.UsePublicIPAddressPermission == dtl.Allow { + if *subnetOverride.UseInVMCreationPermission == hashiDTLVNETSDK.UsagePermissionTypeAllow && *subnetOverride.UsePublicIPAddressPermission == hashiDTLVNETSDK.UsagePermissionTypeAllow { // 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. @@ -351,22 +361,22 @@ func (b *Builder) getSubnetInformation(ctx context.Context, ui packersdk.Ui, azC 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) +func normalizeAzureRegion(name string) string { + return strings.ToLower(strings.Replace(name, " ", "", -1)) +} - if err != nil { - ui.Error(fmt.Sprintf("Failed to parse the token,Error: %s", err.Error())) - return "" +func (b *Builder) managedImageArtifactWithSIGAsDestination(managedImageID string) (*Artifact, error) { + destinationSharedImageGalleryId := "" + if galleryID, ok := b.stateBag.GetOk(constants.ArmManagedImageSharedGalleryId); ok { + destinationSharedImageGalleryId = galleryID.(string) + } else { + return nil, ErrNoImage } - return claims["oid"].(string) - -} -func normalizeAzureRegion(name string) string { - return strings.ToLower(strings.Replace(name, " ", "", -1)) + return NewManagedImageArtifactWithSIGAsDestination(b.config.OSType, + b.config.ManagedImageResourceGroupName, + b.config.ManagedImageName, + b.config.Location, + managedImageID, + destinationSharedImageGalleryId) } diff --git a/builder/azure/dtl/builder_acc_test.go b/builder/azure/dtl/builder_acc_test.go index adf171dd..0cad0f03 100644 --- a/builder/azure/dtl/builder_acc_test.go +++ b/builder/azure/dtl/builder_acc_test.go @@ -36,7 +36,7 @@ import ( "github.com/hashicorp/packer-plugin-sdk/acctest" ) -func TestBuilderAcc_ManagedDisk_Windows(t *testing.T) { +func TestDTLBuilderAcc_ManagedDisk_Windows(t *testing.T) { t.Parallel() acctest.TestPlugin(t, &acctest.PluginTestCase{ Name: "test-azure-managedisk-windows", @@ -52,7 +52,7 @@ func TestBuilderAcc_ManagedDisk_Windows(t *testing.T) { }, }) } -func TestBuilderAcc_ManagedDisk_Linux_Artifacts(t *testing.T) { +func TestDTLBuilderAcc_ManagedDisk_Linux_Artifacts(t *testing.T) { t.Parallel() acctest.TestPlugin(t, &acctest.PluginTestCase{ Name: "test-azure-managedisk-linux", diff --git a/builder/azure/dtl/config.go b/builder/azure/dtl/config.go index bf0cf8ba..d4968045 100644 --- a/builder/azure/dtl/config.go +++ b/builder/azure/dtl/config.go @@ -218,7 +218,7 @@ type Config struct { // 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. - AzureTags map[string]*string `mapstructure:"azure_tags" required:"false"` + AzureTags map[string]string `mapstructure:"azure_tags" required:"false"` // Used for creating images from Marketplace images. Please refer to // [Deploy an image with Marketplace @@ -325,6 +325,10 @@ func (c *Config) isManagedImage() bool { return c.ManagedImageName != "" } +func (c *Config) isPublishToSIG() bool { + return c.SharedGalleryDestination.SigDestinationGalleryName != "" +} + func (c *Config) toVirtualMachineCaptureParameters() *compute.VirtualMachineCaptureParameters { return &compute.VirtualMachineCaptureParameters{ DestinationContainerName: &c.CaptureContainerName, @@ -575,8 +579,8 @@ func assertTagProperties(c *Config, errs *packersdk.MultiError) { 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))) + if len(v) > 256 { + errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("the tag name %q exceeds (%d) the 256 character limit", v, len(v))) } } } diff --git a/builder/azure/dtl/config.hcl2spec.go b/builder/azure/dtl/config.hcl2spec.go index 18a3be9c..e5992e3d 100644 --- a/builder/azure/dtl/config.hcl2spec.go +++ b/builder/azure/dtl/config.hcl2spec.go @@ -76,7 +76,7 @@ type FlatConfig struct { ManagedImageResourceGroupName *string `mapstructure:"managed_image_resource_group_name" required:"true" cty:"managed_image_resource_group_name" hcl:"managed_image_resource_group_name"` ManagedImageName *string `mapstructure:"managed_image_name" required:"true" 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"` + 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"` diff --git a/builder/azure/dtl/config_test.go b/builder/azure/dtl/config_test.go index 49a69613..f8cf79ab 100644 --- a/builder/azure/dtl/config_test.go +++ b/builder/azure/dtl/config_test.go @@ -266,13 +266,13 @@ func TestConfigShouldAcceptTags(t *testing.T) { } value := config.AzureTags["tag01"] - if *value != "value01" { - t.Errorf("expected AzureTags[\"tag01\"] to have value \"value01\", but got %q", *value) + if value != "value01" { + t.Errorf("expected AzureTags[\"tag01\"] to have value \"value01\", but got %q", value) } value = config.AzureTags["tag02"] - if *value != "value02" { - t.Errorf("expected AzureTags[\"tag02\"] to have value \"value02\", but got %q", *value) + if value != "value02" { + t.Errorf("expected AzureTags[\"tag02\"] to have value \"value02\", but got %q", value) } } diff --git a/builder/azure/dtl/resource_resolver.go b/builder/azure/dtl/resource_resolver.go index 46c56a86..c41cf6c5 100644 --- a/builder/azure/dtl/resource_resolver.go +++ b/builder/azure/dtl/resource_resolver.go @@ -16,129 +16,48 @@ import ( "fmt" "strings" - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2018-04-01/compute" + "github.com/hashicorp/go-azure-helpers/resourcemanager/commonids" + hashiImagesSDK "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-01/images" ) type resourceResolver struct { - client *AzureClient - findVirtualNetworkResourceGroup func(*AzureClient, string) (string, error) - findVirtualNetworkSubnet func(*AzureClient, string, string) (string, error) + client *AzureClient } func newResourceResolver(client *AzureClient) *resourceResolver { return &resourceResolver{ - client: client, - findVirtualNetworkResourceGroup: findVirtualNetworkResourceGroup, - findVirtualNetworkSubnet: findVirtualNetworkSubnet, + client: client, } } 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) + image, err := findManagedImageByName(s.client, c.ClientConfig.SubscriptionID, c.CustomManagedImageName, c.CustomManagedImageResourceGroupName) if err != nil { return err } - c.customManagedImageID = *image.ID + 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) +func findManagedImageByName(client *AzureClient, name, subscriptionId, resourceGroupName string) (*hashiImagesSDK.Image, error) { + id := commonids.NewResourceGroupID(subscriptionId, resourceGroupName) + images, err := client.ImagesClient.ListByResourceGroupComplete(context.TODO(), id) if err != nil { return nil, err } - for images.NotDone() { - image := images.Value() + for _, image := range images.Items { 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_capture_image.go b/builder/azure/dtl/step_capture_image.go index 4272410b..be575506 100644 --- a/builder/azure/dtl/step_capture_image.go +++ b/builder/azure/dtl/step_capture_image.go @@ -7,8 +7,7 @@ import ( "context" "fmt" - "github.com/Azure/azure-sdk-for-go/services/devtestlabs/mgmt/2018-09-15/dtl" - + hashiDTLCustomImagesSDK "github.com/hashicorp/go-azure-sdk/resource-manager/devtestlab/2018-09-15/customimages" "github.com/hashicorp/packer-plugin-azure/builder/azure/common/constants" "github.com/hashicorp/packer-plugin-sdk/multistep" packersdk "github.com/hashicorp/packer-plugin-sdk/packer" @@ -17,7 +16,6 @@ import ( 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) @@ -26,9 +24,6 @@ type StepCaptureImage struct { 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) @@ -51,46 +46,43 @@ func (s *StepCaptureImage) captureImageFromVM(ctx context.Context) error { s.config.LabName, s.config.tmpComputeName) - customImageProperties := dtl.CustomImageProperties{} + customImageProperties := hashiDTLCustomImagesSDK.CustomImageProperties{} if s.config.OSType == constants.Target_Linux { - deprovision := dtl.DeprovisionRequested + deprovision := hashiDTLCustomImagesSDK.LinuxOsStateDeprovisionRequested if s.config.SkipSysprep { - deprovision = dtl.DeprovisionApplied + deprovision = hashiDTLCustomImagesSDK.LinuxOsStateDeprovisionApplied } - customImageProperties = dtl.CustomImageProperties{ - VM: &dtl.CustomImagePropertiesFromVM{ - LinuxOsInfo: &dtl.LinuxOsInfo{ - LinuxOsState: deprovision, + customImageProperties = hashiDTLCustomImagesSDK.CustomImageProperties{ + VM: &hashiDTLCustomImagesSDK.CustomImagePropertiesFromVM{ + LinuxOsInfo: &hashiDTLCustomImagesSDK.LinuxOsInfo{ + LinuxOsState: &deprovision, }, - SourceVMID: &imageID, + SourceVMId: &imageID, }, } } else if s.config.OSType == constants.Target_Windows { - deprovision := dtl.SysprepRequested + deprovision := hashiDTLCustomImagesSDK.WindowsOsStateSysprepRequested if s.config.SkipSysprep { - deprovision = dtl.SysprepApplied + deprovision = hashiDTLCustomImagesSDK.WindowsOsStateSysprepApplied } - customImageProperties = dtl.CustomImageProperties{ - VM: &dtl.CustomImagePropertiesFromVM{ - WindowsOsInfo: &dtl.WindowsOsInfo{ - WindowsOsState: deprovision, + customImageProperties = hashiDTLCustomImagesSDK.CustomImageProperties{ + VM: &hashiDTLCustomImagesSDK.CustomImagePropertiesFromVM{ + WindowsOsInfo: &hashiDTLCustomImagesSDK.WindowsOsInfo{ + WindowsOsState: &deprovision, }, - SourceVMID: &imageID, + SourceVMId: &imageID, }, } } - customImage := &dtl.CustomImage{ - Name: &s.config.ManagedImageName, - CustomImageProperties: &customImageProperties, + customImage := &hashiDTLCustomImagesSDK.CustomImage{ + Name: &s.config.ManagedImageName, + Properties: customImageProperties, } - f, err := s.client.DtlCustomImageClient.CreateOrUpdate(ctx, s.config.LabResourceGroupName, s.config.LabName, s.config.ManagedImageName, *customImage) - if err == nil { - s.say("Waiting for Capture Image to complete") - err = f.WaitForCompletionRef(ctx, s.client.DtlCustomImageClient.Client) - } + customImageId := hashiDTLCustomImagesSDK.NewCustomImageID(s.config.ClientConfig.SubscriptionID, s.config.LabResourceGroupName, s.config.LabName, s.config.ManagedImageName) + err := s.client.DtlMetaClient.CustomImages.CreateOrUpdateThenPoll(ctx, customImageId, *customImage) if err != nil { s.say("Error from Capture Image") s.say(s.client.LastError.Error()) @@ -119,17 +111,6 @@ func (s *StepCaptureImage) Run(ctx context.Context, state multistep.StateBag) mu 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 } diff --git a/builder/azure/dtl/step_delete_virtual_machine.go b/builder/azure/dtl/step_delete_virtual_machine.go index 44bc24c0..8f809e03 100644 --- a/builder/azure/dtl/step_delete_virtual_machine.go +++ b/builder/azure/dtl/step_delete_virtual_machine.go @@ -7,6 +7,7 @@ import ( "context" "fmt" + hashiDTLVMSDK "github.com/hashicorp/go-azure-sdk/resource-manager/devtestlab/2018-09-15/virtualmachines" "github.com/hashicorp/packer-plugin-azure/builder/azure/common/constants" "github.com/hashicorp/packer-plugin-sdk/multistep" packersdk "github.com/hashicorp/packer-plugin-sdk/packer" @@ -15,7 +16,7 @@ import ( type StepDeleteVirtualMachine struct { client *AzureClient config *Config - delete func(ctx context.Context, resourceGroupName string, computeName string, state multistep.StateBag) error + delete func(ctx context.Context, resourceGroupName string, subscriptionId string, labName string, computeName string) error say func(message string) error func(e error) } @@ -32,11 +33,9 @@ func NewStepDeleteVirtualMachine(client *AzureClient, ui packersdk.Ui, config *C 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) - } +func (s *StepDeleteVirtualMachine) deleteVirtualMachine(ctx context.Context, subscriptionId string, labName string, resourceGroupName string, vmName string) error { + vmId := hashiDTLVMSDK.NewVirtualMachineID(subscriptionId, resourceGroupName, labName, vmName) + err := s.client.DtlMetaClient.VirtualMachines.DeleteThenPoll(ctx, vmId) if err != nil { s.say("Error from delete VM") s.say(s.client.LastError.Error()) @@ -50,11 +49,14 @@ func (s *StepDeleteVirtualMachine) Run(ctx context.Context, state multistep.Stat var resourceGroupName = state.Get(constants.ArmResourceGroupName).(string) var computeName = state.Get(constants.ArmComputeName).(string) + var dtlLabName = state.Get(constants.DtlLabName).(string) + var subscriptionId = state.Get(constants.ArmSubscription).(string) s.say(fmt.Sprintf(" -> ResourceGroupName : '%s'", resourceGroupName)) + s.say(fmt.Sprintf(" -> ComputeName : '%s'", computeName)) - err := s.deleteVirtualMachine(ctx, resourceGroupName, computeName, state) + err := s.deleteVirtualMachine(ctx, subscriptionId, dtlLabName, resourceGroupName, computeName) s.say("Deleting virtual machine ...Complete") return processStepResult(err, s.error, state) diff --git a/builder/azure/dtl/step_deploy_template.go b/builder/azure/dtl/step_deploy_template.go index ef8407fb..b0e13747 100644 --- a/builder/azure/dtl/step_deploy_template.go +++ b/builder/azure/dtl/step_deploy_template.go @@ -5,29 +5,29 @@ package dtl import ( "context" - "errors" "fmt" - "net/url" "strings" "time" - "github.com/Azure/azure-sdk-for-go/services/devtestlabs/mgmt/2018-09-15/dtl" + "github.com/hashicorp/go-azure-helpers/resourcemanager/commonids" + + hashiLabsSDK "github.com/hashicorp/go-azure-sdk/resource-manager/devtestlab/2018-09-15/labs" + hashiDTLVMSDK "github.com/hashicorp/go-azure-sdk/resource-manager/devtestlab/2018-09-15/virtualmachines" + "github.com/hashicorp/go-azure-sdk/resource-manager/network/2022-09-01/networkinterfaces" "github.com/hashicorp/packer-plugin-azure/builder/azure/common/constants" "github.com/hashicorp/packer-plugin-sdk/multistep" packersdk "github.com/hashicorp/packer-plugin-sdk/packer" + "github.com/hashicorp/packer-plugin-sdk/retry" ) 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 + client *AzureClient + deploy func(ctx context.Context, resourceGroupName string, deploymentName string, state multistep.StateBag) 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 { @@ -41,24 +41,26 @@ func NewStepDeployTemplate(client *AzureClient, ui packersdk.Ui, config *Config, } 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, "") + subscriptionId := s.config.ClientConfig.SubscriptionID + labName := s.config.LabName + // TODO Talk to Tom(s) about this, we have to have two different Labs IDs in different calls, so we can probably move this into the commonids package + labResourceId := hashiDTLVMSDK.NewLabID(subscriptionId, resourceGroupName, labName) + labId := hashiLabsSDK.NewLabID(subscriptionId, s.config.tmpResourceGroupName, labName) + vmlistPage, err := s.client.DtlMetaClient.VirtualMachines.List(ctx, labResourceId, hashiDTLVMSDK.DefaultListOperationOptions()) 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 { + vmList := vmlistPage.Model + for _, vm := range *vmList { + if *vm.Name == s.config.tmpComputeName { return fmt.Errorf("Error: Virtual Machine %s already exists. Please use another name", s.config.tmpComputeName) } } @@ -69,17 +71,15 @@ func (s *StepDeployTemplate) deployTemplate(ctx context.Context, resourceGroupNa 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) - } + err = s.client.DtlMetaClient.Labs.CreateEnvironmentThenPoll(ctx, labId, *labMachine) 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) + vmResourceId := hashiDTLVMSDK.NewVirtualMachineID(subscriptionId, s.config.tmpResourceGroupName, labName, s.config.tmpComputeName) + vm, err := s.client.DtlMetaClient.VirtualMachines.Get(ctx, vmResourceId, hashiDTLVMSDK.GetOperationOptions{Expand: &expand}) if err != nil { s.say(s.client.LastError.Error()) } @@ -87,14 +87,16 @@ func (s *StepDeployTemplate) deployTemplate(ctx context.Context, resourceGroupNa // set tmpFQDN to the PrivateIP or to the real FQDN depending on // publicIP being allowed or not if s.config.DisallowPublicIP { - resp, err := s.client.InterfacesClient.Get(ctx, s.config.tmpResourceGroupName, s.config.tmpNicName, "") + interfaceID := commonids.NewNetworkInterfaceID(subscriptionId, resourceGroupName, s.config.tmpNicName) + resp, err := s.client.NetworkMetaClient.NetworkInterfaces.Get(ctx, interfaceID, networkinterfaces.DefaultGetOperationOptions()) if err != nil { s.say(s.client.LastError.Error()) return err } - s.config.tmpFQDN = *(*resp.IPConfigurations)[0].PrivateIPAddress + // TODO This operation seems kinda off, but I don't wanna spend time digging into it right now + s.config.tmpFQDN = *(*resp.Model.Properties.IPConfigurations)[0].Properties.PrivateIPAddress } else { - s.config.tmpFQDN = *vm.Fqdn + s.config.tmpFQDN = *vm.Model.Properties.Fqdn } s.say(fmt.Sprintf(" -> VM FQDN/IP : '%s'", s.config.tmpFQDN)) state.Put(constants.SSHHost, s.config.tmpFQDN) @@ -111,39 +113,42 @@ func (s *StepDeployTemplate) deployTemplate(ctx context.Context, resourceGroupNa winrma) var hostname = "hostName" - dp := &dtl.ArtifactParameterProperties{} + dp := &hashiDTLVMSDK.ArtifactParameterProperties{} dp.Name = &hostname dp.Value = &s.config.tmpFQDN - dparams := []dtl.ArtifactParameterProperties{*dp} + dparams := []hashiDTLVMSDK.ArtifactParameterProperties{*dp} - winrmArtifact := &dtl.ArtifactInstallProperties{ + winrmArtifact := &hashiDTLVMSDK.ArtifactInstallProperties{ ArtifactTitle: &winrma, - ArtifactID: &artifactid, + ArtifactId: &artifactid, Parameters: &dparams, } - dtlArtifacts := []dtl.ArtifactInstallProperties{*winrmArtifact} - dtlArtifactsRequest := dtl.ApplyArtifactsRequest{Artifacts: &dtlArtifacts} - - // infinite loop until the artifacts have been applied - for { - f, err := s.client.DtlVirtualMachineClient.ApplyArtifacts(ctx, s.config.tmpResourceGroupName, s.config.LabName, s.config.tmpComputeName, dtlArtifactsRequest) - if err == nil { - s.say("WinRM artifact deployment started, waiting for completion") - err = f.WaitForCompletionRef(ctx, s.client.DtlVirtualMachineClient.Client) - if err != nil { - s.say(s.client.LastError.Error()) - return err - } - break - } else { - s.say("WinRM artifact deployment failed, sleeping a minute and retrying") - time.Sleep(60 * time.Second) + dtlArtifacts := []hashiDTLVMSDK.ArtifactInstallProperties{*winrmArtifact} + dtlArtifactsRequest := hashiDTLVMSDK.ApplyArtifactsRequest{Artifacts: &dtlArtifacts} + + // TODO this was an infinite loop, I have seen apply artifacts fail + // But this needs a bit further validation into why it fails and + // How we can avoid the need for a retry backoff + // But a retry backoff is much more preferable to an infinite loop + + retryConfig := retry.Config{ + Tries: 5, + RetryDelay: (&retry.Backoff{InitialBackoff: 5 * time.Second, MaxBackoff: 60 * time.Second, Multiplier: 1.5}).Linear, + } + err = retryConfig.Run(ctx, func(ctx context.Context) error { + err := s.client.DtlMetaClient.VirtualMachines.ApplyArtifactsThenPoll(ctx, vmResourceId, dtlArtifactsRequest) + if err != nil { + s.say("WinRM artifact deployment failed, retrying") } + return nil + }) + if err != nil { + return err } } - xs := strings.Split(*vm.LabVirtualMachineProperties.ComputeID, "/") + xs := strings.Split(*vm.Model.Properties.ComputeId, "/") s.config.VMCreationResourceGroup = xs[4] // Resuing the Resource group name from common constants as all steps depend on it. @@ -166,85 +171,7 @@ func (s *StepDeployTemplate) Run(ctx context.Context, state multistep.StateBag) 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 -} - -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 + // TODO are there any resources created in DTL builds we should tear down? + // There was teardown code from the ARM builder copy pasted in but it was never called } diff --git a/builder/azure/dtl/step_power_off_compute.go b/builder/azure/dtl/step_power_off_compute.go index b72b11ae..497824d6 100644 --- a/builder/azure/dtl/step_power_off_compute.go +++ b/builder/azure/dtl/step_power_off_compute.go @@ -7,6 +7,7 @@ import ( "context" "fmt" + hashiDTLVMSDK "github.com/hashicorp/go-azure-sdk/resource-manager/devtestlab/2018-09-15/virtualmachines" "github.com/hashicorp/packer-plugin-azure/builder/azure/common/constants" "github.com/hashicorp/packer-plugin-sdk/multistep" packersdk "github.com/hashicorp/packer-plugin-sdk/packer" @@ -35,10 +36,8 @@ func NewStepPowerOffCompute(client *AzureClient, ui packersdk.Ui, config *Config 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) - } + vmResourceId := hashiDTLVMSDK.NewVirtualMachineID(s.config.ClientConfig.SubscriptionID, s.config.tmpResourceGroupName, labName, computeName) + err := s.client.DtlMetaClient.VirtualMachines.StopThenPoll(ctx, vmResourceId) if err != nil { s.say(s.client.LastError.Error()) } diff --git a/builder/azure/dtl/step_publish_to_shared_image_gallery.go b/builder/azure/dtl/step_publish_to_shared_image_gallery.go index 2c08e1bf..41450773 100644 --- a/builder/azure/dtl/step_publish_to_shared_image_gallery.go +++ b/builder/azure/dtl/step_publish_to_shared_image_gallery.go @@ -7,7 +7,7 @@ import ( "context" "fmt" - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-03-01/compute" + hashiGalleryImageVersionsSDK "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-03/galleryimageversions" "github.com/hashicorp/packer-plugin-azure/builder/azure/common/constants" "github.com/hashicorp/packer-plugin-sdk/multistep" packersdk "github.com/hashicorp/packer-plugin-sdk/packer" @@ -15,7 +15,7 @@ import ( 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) + publish func(ctx context.Context, subscriptionID, managedImageID, sigDestinationResourceGroup, sigDestinationGalleryName, sigDestinationImageName, sigDestinationImageVersion string, sigReplicationRegions []string, location string, tags map[string]string) (string, error) say func(message string) error func(e error) toSIG func() bool @@ -39,52 +39,45 @@ func NewStepPublishToSharedImageGallery(client *AzureClient, ui packersdk.Ui, co 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) { +func (s *StepPublishToSharedImageGallery) publishToSig(ctx context.Context, subscriptionID, managedImageID, sigDestinationResourceGroup, sigDestinationGalleryName, sigDestinationImageName, sigDestinationImageVersion string, sigReplicationRegions []string, location string, tags map[string]string) (string, error) { - replicationRegions := make([]compute.TargetRegion, len(miSigReplicationRegions)) - for i, v := range miSigReplicationRegions { + replicationRegions := make([]hashiGalleryImageVersionsSDK.TargetRegion, len(sigReplicationRegions)) + for i, v := range sigReplicationRegions { regionName := v - replicationRegions[i] = compute.TargetRegion{Name: ®ionName} + replicationRegions[i] = hashiGalleryImageVersionsSDK.TargetRegion{Name: regionName} } - galleryImageVersion := compute.GalleryImageVersion{ - Location: &location, - Tags: tags, - GalleryImageVersionProperties: &compute.GalleryImageVersionProperties{ - PublishingProfile: &compute.GalleryImageVersionPublishingProfile{ - Source: &compute.GalleryArtifactSource{ - ManagedImage: &compute.ManagedArtifact{ - ID: &mdiID, - }, + galleryImageVersion := hashiGalleryImageVersionsSDK.GalleryImageVersion{ + Location: location, + Tags: &tags, + Properties: &hashiGalleryImageVersionsSDK.GalleryImageVersionProperties{ + StorageProfile: hashiGalleryImageVersionsSDK.GalleryImageVersionStorageProfile{ + Source: &hashiGalleryImageVersionsSDK.GalleryArtifactVersionFullSource{ + Id: &managedImageID, }, + }, + PublishingProfile: &hashiGalleryImageVersionsSDK.GalleryArtifactPublishingProfileBase{ 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) + galleryImageVersionId := hashiGalleryImageVersionsSDK.NewImageVersionID(subscriptionID, sigDestinationResourceGroup, sigDestinationGalleryName, sigDestinationImageName, sigDestinationImageVersion) + err := s.client.GalleryImageVersionsClient.CreateOrUpdateThenPoll(ctx, galleryImageVersionId, galleryImageVersion) if err != nil { s.say(s.client.LastError.Error()) return "", err } - - createdSGImageVersion, err := f.Result(s.client.GalleryImageVersionsClient) + createdSIGImageVersion, err := s.client.GalleryImageVersionsClient.Get(ctx, galleryImageVersionId, hashiGalleryImageVersionsSDK.DefaultGetOperationOptions()) 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 + s.say(fmt.Sprintf(" -> Shared Gallery Image Version ID : '%s'", *(createdSIGImageVersion.Model.Id))) + return *(createdSIGImageVersion.Model.Id), nil } func (s *StepPublishToSharedImageGallery) Run(ctx context.Context, stateBag multistep.StateBag) multistep.StepAction { @@ -99,20 +92,20 @@ func (s *StepPublishToSharedImageGallery) Run(ctx context.Context, stateBag mult 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 tags = stateBag.Get(constants.ArmNewSDKTags).(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) + var managedImageID = 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(" -> MDI ID used for SIG publish : '%s'", managedImageID)) 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) + createdGalleryImageVersionID, err := s.publish(ctx, managedImageSubscription, managedImageID, miSigPubRg, miSIGalleryName, miSGImageName, miSGImageVersion, miSigReplicationRegions, location, tags) if err != nil { stateBag.Put(constants.Error, err) diff --git a/builder/azure/dtl/template_factory.go b/builder/azure/dtl/template_factory.go index c0c96f3e..6fe6dd0c 100644 --- a/builder/azure/dtl/template_factory.go +++ b/builder/azure/dtl/template_factory.go @@ -6,10 +6,10 @@ package dtl import ( "fmt" - "github.com/Azure/azure-sdk-for-go/services/devtestlabs/mgmt/2018-09-15/dtl" + hashiDTLLabsSDK "github.com/hashicorp/go-azure-sdk/resource-manager/devtestlab/2018-09-15/labs" ) -type templateFactoryFuncDtl func(*Config) (*dtl.LabVirtualMachineCreationParameter, error) +type templateFactoryFuncDtl func(*Config) (*hashiDTLLabsSDK.LabVirtualMachineCreationParameter, error) func newBool(val bool) *bool { b := true @@ -32,9 +32,9 @@ func getCustomImageId(config *Config) *string { return nil } -func GetVirtualMachineDeployment(config *Config) (*dtl.LabVirtualMachineCreationParameter, error) { +func GetVirtualMachineDeployment(config *Config) (*hashiDTLLabsSDK.LabVirtualMachineCreationParameter, error) { - galleryImageRef := dtl.GalleryImageReference{ + galleryImageRef := hashiDTLLabsSDK.GalleryImageReference{ Offer: &config.ImageOffer, Publisher: &config.ImagePublisher, Sku: &config.ImageSku, @@ -48,7 +48,7 @@ func GetVirtualMachineDeployment(config *Config) (*dtl.LabVirtualMachineCreation config.LabName, config.LabVirtualNetworkName) - dtlArtifacts := []dtl.ArtifactInstallProperties{} + dtlArtifacts := []hashiDTLLabsSDK.ArtifactInstallProperties{} if config.DtlArtifacts != nil { for i := range config.DtlArtifacts { @@ -62,51 +62,50 @@ func GetVirtualMachineDeployment(config *Config) (*dtl.LabVirtualMachineCreation config.DtlArtifacts[i].RepositoryName, config.DtlArtifacts[i].ArtifactName) - dparams := []dtl.ArtifactParameterProperties{} + dparams := []hashiDTLLabsSDK.ArtifactParameterProperties{} for j := range config.DtlArtifacts[i].Parameters { - dp := &dtl.ArtifactParameterProperties{} + dp := &hashiDTLLabsSDK.ArtifactParameterProperties{} dp.Name = &config.DtlArtifacts[i].Parameters[j].Name dp.Value = &config.DtlArtifacts[i].Parameters[j].Value dparams = append(dparams, *dp) } - dtlArtifact := &dtl.ArtifactInstallProperties{ + dtlArtifact := &hashiDTLLabsSDK.ArtifactInstallProperties{ ArtifactTitle: &config.DtlArtifacts[i].ArtifactName, - ArtifactID: &config.DtlArtifacts[i].ArtifactId, + ArtifactId: &config.DtlArtifacts[i].ArtifactId, Parameters: &dparams, } dtlArtifacts = append(dtlArtifacts, *dtlArtifact) } } - labMachineProps := &dtl.LabVirtualMachineCreationParameterProperties{ - CreatedByUserID: &config.ClientConfig.ClientID, - OwnerObjectID: &config.ClientConfig.ObjectID, - OsType: &config.OSType, + labMachineProps := &hashiDTLLabsSDK.LabVirtualMachineCreationParameterProperties{ + OwnerUserPrincipalName: &config.ClientConfig.ClientID, + OwnerObjectId: &config.ClientConfig.ObjectID, Size: &config.VMSize, UserName: &config.UserName, Password: &config.Password, - SSHKey: &config.sshAuthorizedKey, - IsAuthenticationWithSSHKey: newBool(true), + SshKey: &config.sshAuthorizedKey, + IsAuthenticationWithSshKey: newBool(true), LabSubnetName: &config.LabSubnetName, - LabVirtualNetworkID: &labVirtualNetworkID, + LabVirtualNetworkId: &labVirtualNetworkID, DisallowPublicIPAddress: &config.DisallowPublicIP, GalleryImageReference: &galleryImageRef, - CustomImageID: getCustomImageId(config), - PlanID: &config.PlanID, + CustomImageId: getCustomImageId(config), + PlanId: &config.PlanID, - AllowClaim: newBool(false), - StorageType: &config.StorageType, - VirtualMachineCreationSource: dtl.FromGalleryImage, - Artifacts: &dtlArtifacts, + AllowClaim: newBool(false), + StorageType: &config.StorageType, + Artifacts: &dtlArtifacts, } - labMachine := &dtl.LabVirtualMachineCreationParameter{ + labMachine := &hashiDTLLabsSDK.LabVirtualMachineCreationParameter{ Name: &config.tmpComputeName, Location: &config.Location, - Tags: config.AzureTags, - LabVirtualMachineCreationParameterProperties: labMachineProps, + // TODO + //Tags: config.AzureTags, + Properties: labMachineProps, } return labMachine, nil diff --git a/docs-partials/builder/azure/dtl/Config-not-required.mdx b/docs-partials/builder/azure/dtl/Config-not-required.mdx index fb2859df..9c51166c 100644 --- a/docs-partials/builder/azure/dtl/Config-not-required.mdx +++ b/docs-partials/builder/azure/dtl/Config-not-required.mdx @@ -87,7 +87,7 @@ 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 +- `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. diff --git a/go.mod b/go.mod index 662bf36c..eb3203df 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,6 @@ require ( github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect github.com/Azure/go-autorest/autorest/to v0.4.0 github.com/approvals/go-approval-tests v0.0.0-20210131072903-38d0b0ec12b1 - github.com/dnaeon/go-vcr v1.1.0 // indirect github.com/golang-jwt/jwt v3.2.2+incompatible github.com/google/go-cmp v0.5.9 github.com/hashicorp/go-azure-helpers v0.56.0 diff --git a/go.sum b/go.sum index a505f8c9..060fd06d 100644 --- a/go.sum +++ b/go.sum @@ -133,8 +133,6 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZm github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8= 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.1.0 h1:ReYa/UBrRyQdant9B4fNHGoCNKw6qh6P0fsdGmZpR7c= -github.com/dnaeon/go-vcr v1.1.0/go.mod h1:M7tiix8f0r6mKKJ3Yq/kqU1OYf3MnfmBWVbPx/yU9ko= github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/docker v1.4.2-0.20200319182547-c7ad2b866182/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= @@ -413,7 +411,6 @@ github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJ 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/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d h1:VhgPp6v9qf9Agr/56bj7Y/xa04UccTW04VP0Qed4vnQ= diff --git a/provisioner/azure-dtlartifact/provisioner.go b/provisioner/azure-dtlartifact/provisioner.go index e84c3092..d240fadc 100644 --- a/provisioner/azure-dtlartifact/provisioner.go +++ b/provisioner/azure-dtlartifact/provisioner.go @@ -11,11 +11,11 @@ import ( "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-plugin-azure/builder/azure/common/client" dtlBuilder "github.com/hashicorp/packer-plugin-azure/builder/azure/dtl" + hashiDTLVMSDK "github.com/hashicorp/go-azure-sdk/resource-manager/devtestlab/2018-09-15/virtualmachines" packersdk "github.com/hashicorp/packer-plugin-sdk/packer" "github.com/hashicorp/packer-plugin-sdk/common" @@ -125,20 +125,25 @@ func (p *Provisioner) Provision(ctx context.Context, ui packersdk.Ui, comm packe return err } - spnCloud, err := p.config.ClientConfig.GetServicePrincipalToken(ui.Say, p.config.ClientConfig.CloudEnvironment().ResourceManagerEndpoint) - if err != nil { - return err + // Pass in relevant auth information for hashicorp/go-azure-sdk + authOptions := dtlBuilder.NewSDKAuthOptions{ + AuthType: p.config.ClientConfig.AuthType(), + ClientID: p.config.ClientConfig.ClientID, + ClientSecret: p.config.ClientConfig.ClientSecret, + ClientJWT: p.config.ClientConfig.ClientJWT, + ClientCertPath: p.config.ClientConfig.ClientCertPath, + TenantID: p.config.ClientConfig.TenantID, + SubscriptionID: p.config.ClientConfig.SubscriptionID, } - ui.Message("Creating Azure DevTestLab (DTL) client ...") - azureClient, err := dtlBuilder.NewAzureClient( + azureClient, _, err := dtlBuilder.NewAzureClient( + ctx, p.config.ClientConfig.SubscriptionID, - "", - p.config.ClientConfig.CloudEnvironment(), + p.config.ClientConfig.NewCloudEnvironment(), p.config.PollingDurationTimeout, p.config.PollingDurationTimeout, p.config.PollingDurationTimeout, - spnCloud) + authOptions) if err != nil { ui.Say(fmt.Sprintf("Error saving debug key: %s", err)) @@ -146,7 +151,7 @@ func (p *Provisioner) Provision(ctx context.Context, ui packersdk.Ui, comm packe } ui.Say("Installing Artifact DTL") - dtlArtifacts := []dtl.ArtifactInstallProperties{} + dtlArtifacts := []hashiDTLVMSDK.ArtifactInstallProperties{} if p.config.DtlArtifacts != nil { for i := range p.config.DtlArtifacts { @@ -156,16 +161,16 @@ func (p *Provisioner) Provision(ctx context.Context, ui packersdk.Ui, comm packe p.config.LabName, p.config.DtlArtifacts[i].ArtifactName) - dparams := []dtl.ArtifactParameterProperties{} + dparams := []hashiDTLVMSDK.ArtifactParameterProperties{} for j := range p.config.DtlArtifacts[i].Parameters { - dp := &dtl.ArtifactParameterProperties{} + dp := &hashiDTLVMSDK.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, + Aip := hashiDTLVMSDK.ArtifactInstallProperties{ + ArtifactId: &p.config.DtlArtifacts[i].ArtifactId, Parameters: &dparams, ArtifactTitle: &p.config.DtlArtifacts[i].ArtifactName, } @@ -173,19 +178,19 @@ func (p *Provisioner) Provision(ctx context.Context, ui packersdk.Ui, comm packe } } - dtlApplyArifactRequest := dtl.ApplyArtifactsRequest{ + dtlApplyArifactRequest := hashiDTLVMSDK.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) - } + vmResourceId := hashiDTLVMSDK.NewVirtualMachineID(p.config.ClientConfig.SubscriptionID, p.config.ResourceGroupName, p.config.LabName, p.config.VMName) + err = azureClient.DtlMetaClient.VirtualMachines.ApplyArtifactsThenPoll(ctx, vmResourceId, dtlApplyArifactRequest) + if err != nil { ui.Say(fmt.Sprintf("Error Applying artifact: %s", err)) } + ui.Say("Aftifact installed") return err } From d49114773292a6cb21fa61cb0e6f56dbcfdd9aa8 Mon Sep 17 00:00:00 2001 From: Jenna Goldstrich Date: Fri, 28 Jul 2023 17:00:23 -0700 Subject: [PATCH 06/15] Port CHRoot Builder and rename hashi* imports ==== EOD WIP: I've ported all the client code over, on Monday I will test that it works in a VM before porting the unit tests as the mocking strategy used is heavily reliant on auto rest, and I want to port it to stretchify mocks that don't care as much about the autorest specifics Don't use Subscription ID as Snapshot URL Large commit: Chroot unit tests passing, azure-sdk-for-go no longer imported by go.mod, remove references to device code, clear up missing pieces of DTL builder Fix linux disk attatcher Remove unnused mock that I added earlier Re-add Fetching of Tenant ID Forgot to use New Steps null check was wrong way for SDK model see above Don't try and get tenant ID if we're using CLI auth Address linter, fix linux test StepCreateNewDiskTest -> Return proper autorest future fake remove device code acceptance tests, handle flakey unit test by sorting resulting objects to insure consistant comparison Update builder/azure/chroot/step_verify_source_disk_test.go Co-authored-by: Lucas Bajolet <105649352+lbajolet-hashicorp@users.noreply.github.com> Remove CHRoot Acceptance Test && rename all hashi* imports removing prefix Make generate and fix lint Readd subscription, tenant ID fetching for CLI auth, so we know which subscription to save stuff to, this caused acceptance test failures which this commit fixes Lint --- builder/azure/TODO.md | 16 -- builder/azure/arm/azure_client.go | 193 +++---------- builder/azure/arm/builder.go | 35 +-- builder/azure/arm/builder_acc_test.go | 130 ++------- builder/azure/arm/config.go | 56 ++-- builder/azure/arm/config.hcl2spec.go | 2 - builder/azure/arm/config_test.go | 40 +-- builder/azure/arm/resource_resolver.go | 8 +- builder/azure/arm/step_capture_image.go | 30 +- builder/azure/arm/step_capture_image_test.go | 44 +-- .../azure/arm/step_certificate_in_keyvault.go | 12 +- .../arm/step_certificate_in_keyvault_test.go | 11 +- .../azure/arm/step_create_resource_group.go | 8 +- builder/azure/arm/step_deploy_template.go | 34 +-- .../azure/arm/step_get_additional_disks.go | 10 +- .../arm/step_get_additional_disks_test.go | 24 +- builder/azure/arm/step_get_certificate.go | 4 +- builder/azure/arm/step_get_ip_address.go | 8 +- builder/azure/arm/step_get_os_disk.go | 10 +- builder/azure/arm/step_get_os_disk_test.go | 20 +- .../azure/arm/step_get_source_image_name.go | 10 +- .../arm/step_get_source_image_name_test.go | 22 +- builder/azure/arm/step_power_off_compute.go | 6 +- .../step_publish_to_shared_image_gallery.go | 36 +-- ...ep_publish_to_shared_image_gallery_test.go | 6 +- builder/azure/arm/step_snapshot_data_disks.go | 12 +- builder/azure/arm/step_snapshot_os_disk.go | 12 +- builder/azure/arm/step_validate_template.go | 4 +- builder/azure/chroot/builder.go | 111 ++++---- builder/azure/chroot/builder.hcl2spec.go | 4 +- builder/azure/chroot/builder_test.go | 16 +- builder/azure/chroot/diskattacher.go | 156 +++-------- builder/azure/chroot/diskattacher_freebsd.go | 2 +- builder/azure/chroot/diskattacher_linux.go | 4 +- builder/azure/chroot/diskattacher_other.go | 2 +- builder/azure/chroot/diskattacher_test.go | 96 ------- builder/azure/chroot/diskset.go | 4 +- builder/azure/chroot/diskset_test.go | 2 +- .../shared_image_gallery_destination.go | 2 +- ...ared_image_gallery_destination.hcl2spec.go | 2 +- builder/azure/chroot/step_attach_disk_test.go | 18 +- builder/azure/chroot/step_create_image.go | 78 +++--- .../azure/chroot/step_create_image_test.go | 108 +++----- .../azure/chroot/step_create_new_diskset.go | 148 +++++----- .../chroot/step_create_new_diskset_test.go | 262 ++++++++++-------- .../step_create_shared_image_version.go | 84 +++--- .../step_create_shared_image_version_test.go | 146 +++++----- .../azure/chroot/step_create_snapshotset.go | 58 ++-- .../chroot/step_create_snapshotset_test.go | 217 ++++++--------- .../chroot/step_get_source_image_name.go | 31 ++- .../chroot/step_get_source_image_name_test.go | 102 +++---- .../azure/chroot/step_mount_device_test.go | 3 +- .../step_resolve_plaform_image_version.go | 47 +++- ...step_resolve_plaform_image_version_test.go | 67 +++-- .../step_verify_shared_image_destination.go | 103 ++++--- ...ep_verify_shared_image_destination_test.go | 121 +++----- .../chroot/step_verify_shared_image_source.go | 85 ++++-- .../step_verify_shared_image_source_test.go | 161 +++++------ .../azure/chroot/step_verify_source_disk.go | 34 ++- .../chroot/step_verify_source_disk_test.go | 48 ++-- builder/azure/common/artifact.go | 15 +- .../azure/common/client/azure_authorizer.go | 101 +++++++ .../azure/common/client/azure_client_set.go | 154 +++++----- .../common/client/azure_client_set_mock.go | 69 +++-- builder/azure/common/client/config.go | 237 +++++----------- .../common/client/config_retriever_test.go | 30 +- builder/azure/common/client/config_test.go | 161 +---------- builder/azure/common/client/devicelogin.go | 211 -------------- builder/azure/common/client/platform_image.go | 61 ---- .../common/client/platform_image_test.go | 33 --- builder/azure/common/client/testclient.go | 33 --- builder/azure/common/client/tokenprovider.go | 13 - .../azure/common/client/tokenprovider_cert.go | 168 ----------- .../azure/common/client/tokenprovider_cli.go | 165 ----------- .../client/tokenprovider_devicewflow.go | 42 --- .../azure/common/client/tokenprovider_jwt.go | 46 --- .../azure/common/client/tokenprovider_msi.go | 29 -- .../common/client/tokenprovider_secret.go | 38 --- .../client/tokenprovider_secret_test.go | 43 --- builder/azure/common/vault.go | 141 ---------- builder/azure/common/vault_client_mock.go | 59 ---- builder/azure/common/vault_test.go | 29 -- builder/azure/dtl/azure_client.go | 129 ++------- builder/azure/dtl/builder.go | 29 +- builder/azure/dtl/config.go | 29 +- builder/azure/dtl/config.hcl2spec.go | 2 - builder/azure/dtl/config_test.go | 10 +- builder/azure/dtl/step_capture_image.go | 28 +- .../azure/dtl/step_delete_virtual_machine.go | 4 +- builder/azure/dtl/step_deploy_template.go | 24 +- builder/azure/dtl/step_power_off_compute.go | 5 +- .../step_publish_to_shared_image_gallery.go | 20 +- builder/azure/dtl/template_factory.go | 20 +- builder/azure/pkcs12/pkcs12.go | 3 - .../builder/azure/arm/Config-not-required.mdx | 2 +- .../builder/azure/arm/Spot-not-required.mdx | 2 +- .../azure/chroot/Config-not-required.mdx | 2 +- .../chroot/TargetRegion-not-required.mdx | 2 +- .../common/client/Config-not-required.mdx | 3 - .../builder/azure/common/client/Config.mdx | 5 +- .../builder/azure/dtl/Config-not-required.mdx | 2 +- .../azure-dtlartifact/Config-not-required.mdx | 2 +- docs/builders/arm.mdx | 6 - docs/builders/index.mdx | 22 +- go.mod | 11 +- go.sum | 27 +- provisioner/azure-dtlartifact/provisioner.go | 20 +- .../azure-dtlartifact/provisioner.hcl2spec.go | 2 - 108 files changed, 1860 insertions(+), 3554 deletions(-) delete mode 100644 builder/azure/TODO.md delete mode 100644 builder/azure/chroot/diskattacher_test.go create mode 100644 builder/azure/common/client/azure_authorizer.go delete mode 100644 builder/azure/common/client/devicelogin.go delete mode 100644 builder/azure/common/client/platform_image.go delete mode 100644 builder/azure/common/client/platform_image_test.go delete mode 100644 builder/azure/common/client/testclient.go delete mode 100644 builder/azure/common/client/tokenprovider.go delete mode 100644 builder/azure/common/client/tokenprovider_cert.go delete mode 100644 builder/azure/common/client/tokenprovider_cli.go delete mode 100644 builder/azure/common/client/tokenprovider_devicewflow.go delete mode 100644 builder/azure/common/client/tokenprovider_jwt.go delete mode 100644 builder/azure/common/client/tokenprovider_msi.go delete mode 100644 builder/azure/common/client/tokenprovider_secret.go delete mode 100644 builder/azure/common/client/tokenprovider_secret_test.go delete mode 100644 builder/azure/common/vault.go delete mode 100644 builder/azure/common/vault_client_mock.go delete mode 100644 builder/azure/common/vault_test.go diff --git a/builder/azure/TODO.md b/builder/azure/TODO.md deleted file mode 100644 index f35a4607..00000000 --- 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/azure_client.go b/builder/azure/arm/azure_client.go index 70e735dd..a6ba1172 100644 --- a/builder/azure/arm/azure_client.go +++ b/builder/azure/arm/azure_client.go @@ -14,24 +14,23 @@ import ( "net/http" "github.com/Azure/go-autorest/autorest" - "github.com/golang-jwt/jwt" - hashiImagesSDK "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-01/images" - hashiVMSDK "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-01/virtualmachines" - hashiDisksSDK "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-02/disks" - hashiSnapshotsSDK "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-02/snapshots" - hashiGalleryImagesSDK "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-03/galleryimages" - hashiGalleryImageVersionsSDK "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-03/galleryimageversions" - hashiSecretsSDK "github.com/hashicorp/go-azure-sdk/resource-manager/keyvault/2023-02-01/secrets" - hashiVaultsSDK "github.com/hashicorp/go-azure-sdk/resource-manager/keyvault/2023-02-01/vaults" - hashiNetworkMetaSDK "github.com/hashicorp/go-azure-sdk/resource-manager/network/2022-09-01" - hashiDeploymentOperationsSDK "github.com/hashicorp/go-azure-sdk/resource-manager/resources/2022-09-01/deploymentoperations" - hashiDeploymentsSDK "github.com/hashicorp/go-azure-sdk/resource-manager/resources/2022-09-01/deployments" - hashiGroupsSDK "github.com/hashicorp/go-azure-sdk/resource-manager/resources/2022-09-01/resourcegroups" - hashiStorageAccountsSDK "github.com/hashicorp/go-azure-sdk/resource-manager/storage/2022-09-01/storageaccounts" - "github.com/hashicorp/go-azure-sdk/sdk/auth" + "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-01/images" + "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-01/virtualmachines" + "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-02/disks" + "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-02/snapshots" + "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-03/galleryimages" + "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-03/galleryimageversions" + "github.com/hashicorp/go-azure-sdk/resource-manager/keyvault/2023-02-01/secrets" + "github.com/hashicorp/go-azure-sdk/resource-manager/keyvault/2023-02-01/vaults" + networks "github.com/hashicorp/go-azure-sdk/resource-manager/network/2022-09-01" + "github.com/hashicorp/go-azure-sdk/resource-manager/resources/2022-09-01/deploymentoperations" + "github.com/hashicorp/go-azure-sdk/resource-manager/resources/2022-09-01/deployments" + "github.com/hashicorp/go-azure-sdk/resource-manager/resources/2022-09-01/resourcegroups" + "github.com/hashicorp/go-azure-sdk/resource-manager/storage/2022-09-01/storageaccounts" authWrapper "github.com/hashicorp/go-azure-sdk/sdk/auth/autorest" "github.com/hashicorp/go-azure-sdk/sdk/client/resourcemanager" "github.com/hashicorp/go-azure-sdk/sdk/environments" + commonclient "github.com/hashicorp/packer-plugin-azure/builder/azure/common/client" "github.com/hashicorp/packer-plugin-azure/version" "github.com/hashicorp/packer-plugin-sdk/useragent" giovanniBlobStorageSDK "github.com/tombuildsstuff/giovanni/storage/2020-08-04/blob/blobs" @@ -42,19 +41,19 @@ const ( ) type AzureClient struct { - NetworkMetaClient hashiNetworkMetaSDK.Client - hashiDeploymentsSDK.DeploymentsClient - hashiStorageAccountsSDK.StorageAccountsClient - hashiDeploymentOperationsSDK.DeploymentOperationsClient - hashiImagesSDK.ImagesClient - hashiVMSDK.VirtualMachinesClient - hashiSecretsSDK.SecretsClient - hashiVaultsSDK.VaultsClient - hashiDisksSDK.DisksClient - hashiGroupsSDK.ResourceGroupsClient - hashiSnapshotsSDK.SnapshotsClient - hashiGalleryImageVersionsSDK.GalleryImageVersionsClient - hashiGalleryImagesSDK.GalleryImagesClient + NetworkMetaClient networks.Client + deployments.DeploymentsClient + storageaccounts.StorageAccountsClient + deploymentoperations.DeploymentOperationsClient + images.ImagesClient + virtualmachines.VirtualMachinesClient + secrets.SecretsClient + vaults.VaultsClient + disks.DisksClient + resourcegroups.ResourceGroupsClient + snapshots.SnapshotsClient + galleryimageversions.GalleryImageVersionsClient + galleryimages.GalleryImagesClient GiovanniBlobClient giovanniBlobStorageSDK.Client InspectorMaxLength int LastError azureErrorResponse @@ -76,107 +75,95 @@ func errorCapture(client *AzureClient) autorest.RespondDecorator { } } -// 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. +// TODO Do we need a track 2 version of this method? func byConcatDecorators(decorators ...autorest.RespondDecorator) autorest.RespondDecorator { return func(r autorest.Responder) autorest.Responder { return autorest.DecorateResponder(r, decorators...) } } -type NewSDKAuthOptions struct { - AuthType string - ClientID string - ClientSecret string - ClientJWT string - ClientCertPath string - TenantID string - SubscriptionID string -} - // Returns an Azure Client used for the Azure Resource Manager // Also returns the Azure object ID for the authentication method used in the build -func NewAzureClient(ctx context.Context, isVHDBuild bool, cloud *environments.Environment, sharedGalleryTimeout time.Duration, pollingDuration time.Duration, newSdkAuthOptions NewSDKAuthOptions) (*AzureClient, *string, error) { +func NewAzureClient(ctx context.Context, isVHDBuild bool, cloud *environments.Environment, sharedGalleryTimeout time.Duration, pollingDuration time.Duration, newSdkAuthOptions commonclient.NewSDKAuthOptions) (*AzureClient, *string, error) { var azureClient = &AzureClient{} maxlen := getInspectorMaxLength() if cloud == nil || cloud.ResourceManager == nil { // TODO Throw error message that helps users solve this problem - return nil, nil, fmt.Errorf("Azure Environment not configured correctly") + return nil, nil, fmt.Errorf("azure environment not configured correctly") } resourceManagerEndpoint, _ := cloud.ResourceManager.Endpoint() - resourceManagerAuthorizer, err := buildResourceManagerAuthorizer(ctx, newSdkAuthOptions, *cloud) + resourceManagerAuthorizer, err := commonclient.BuildResourceManagerAuthorizer(ctx, newSdkAuthOptions, *cloud) if err != nil { return nil, nil, err } // Clients that have been ported to hashicorp/go-azure-sdk - azureClient.DisksClient = hashiDisksSDK.NewDisksClientWithBaseURI(*resourceManagerEndpoint) + azureClient.DisksClient = disks.NewDisksClientWithBaseURI(*resourceManagerEndpoint) azureClient.DisksClient.Client.Authorizer = authWrapper.AutorestAuthorizer(resourceManagerAuthorizer) azureClient.DisksClient.Client.RequestInspector = withInspection(maxlen) azureClient.DisksClient.Client.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient)) azureClient.DisksClient.Client.UserAgent = fmt.Sprintf("%s %s", useragent.String(version.AzurePluginVersion.FormattedVersion()), azureClient.DisksClient.Client.UserAgent) azureClient.DisksClient.Client.PollingDuration = pollingDuration - azureClient.VirtualMachinesClient = hashiVMSDK.NewVirtualMachinesClientWithBaseURI(*resourceManagerEndpoint) + azureClient.VirtualMachinesClient = virtualmachines.NewVirtualMachinesClientWithBaseURI(*resourceManagerEndpoint) azureClient.VirtualMachinesClient.Client.Authorizer = authWrapper.AutorestAuthorizer(resourceManagerAuthorizer) azureClient.VirtualMachinesClient.Client.RequestInspector = withInspection(maxlen) azureClient.VirtualMachinesClient.Client.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient)) azureClient.VirtualMachinesClient.Client.UserAgent = fmt.Sprintf("%s %s", useragent.String(version.AzurePluginVersion.FormattedVersion()), azureClient.VirtualMachinesClient.Client.UserAgent) azureClient.VirtualMachinesClient.Client.PollingDuration = pollingDuration - azureClient.SnapshotsClient = hashiSnapshotsSDK.NewSnapshotsClientWithBaseURI(*resourceManagerEndpoint) + azureClient.SnapshotsClient = snapshots.NewSnapshotsClientWithBaseURI(*resourceManagerEndpoint) azureClient.SnapshotsClient.Client.Authorizer = authWrapper.AutorestAuthorizer(resourceManagerAuthorizer) azureClient.SnapshotsClient.Client.RequestInspector = withInspection(maxlen) azureClient.SnapshotsClient.Client.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient)) azureClient.SnapshotsClient.Client.UserAgent = fmt.Sprintf("%s %s", useragent.String(version.AzurePluginVersion.FormattedVersion()), azureClient.SnapshotsClient.Client.UserAgent) azureClient.SnapshotsClient.Client.PollingDuration = pollingDuration - azureClient.SecretsClient = hashiSecretsSDK.NewSecretsClientWithBaseURI(*resourceManagerEndpoint) + azureClient.SecretsClient = secrets.NewSecretsClientWithBaseURI(*resourceManagerEndpoint) azureClient.SecretsClient.Client.Authorizer = authWrapper.AutorestAuthorizer(resourceManagerAuthorizer) azureClient.SecretsClient.Client.RequestInspector = withInspection(maxlen) azureClient.SecretsClient.Client.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient)) azureClient.SecretsClient.Client.UserAgent = fmt.Sprintf("%s %s", useragent.String(version.AzurePluginVersion.FormattedVersion()), azureClient.SecretsClient.Client.UserAgent) azureClient.SecretsClient.Client.PollingDuration = pollingDuration - azureClient.VaultsClient = hashiVaultsSDK.NewVaultsClientWithBaseURI(*resourceManagerEndpoint) + azureClient.VaultsClient = vaults.NewVaultsClientWithBaseURI(*resourceManagerEndpoint) azureClient.VaultsClient.Client.Authorizer = authWrapper.AutorestAuthorizer(resourceManagerAuthorizer) azureClient.VaultsClient.Client.RequestInspector = withInspection(maxlen) azureClient.VaultsClient.Client.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient)) azureClient.VaultsClient.Client.UserAgent = fmt.Sprintf("%s %s", useragent.String(version.AzurePluginVersion.FormattedVersion()), azureClient.VaultsClient.Client.UserAgent) azureClient.VaultsClient.Client.PollingDuration = pollingDuration - azureClient.DeploymentsClient = hashiDeploymentsSDK.NewDeploymentsClientWithBaseURI(*resourceManagerEndpoint) + azureClient.DeploymentsClient = deployments.NewDeploymentsClientWithBaseURI(*resourceManagerEndpoint) azureClient.DeploymentsClient.Client.Authorizer = authWrapper.AutorestAuthorizer(resourceManagerAuthorizer) azureClient.DeploymentsClient.Client.RequestInspector = withInspection(maxlen) azureClient.DeploymentsClient.Client.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient)) azureClient.DeploymentsClient.Client.UserAgent = fmt.Sprintf("%s %s", useragent.String(version.AzurePluginVersion.FormattedVersion()), azureClient.DeploymentsClient.Client.UserAgent) azureClient.DeploymentsClient.Client.PollingDuration = pollingDuration - azureClient.DeploymentOperationsClient = hashiDeploymentOperationsSDK.NewDeploymentOperationsClientWithBaseURI(*resourceManagerEndpoint) + azureClient.DeploymentOperationsClient = deploymentoperations.NewDeploymentOperationsClientWithBaseURI(*resourceManagerEndpoint) azureClient.DeploymentOperationsClient.Client.Authorizer = authWrapper.AutorestAuthorizer(resourceManagerAuthorizer) azureClient.DeploymentOperationsClient.Client.RequestInspector = withInspection(maxlen) azureClient.DeploymentOperationsClient.Client.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient)) azureClient.DeploymentOperationsClient.Client.UserAgent = fmt.Sprintf("%s %s", useragent.String(version.AzurePluginVersion.FormattedVersion()), azureClient.DeploymentOperationsClient.Client.UserAgent) azureClient.DeploymentOperationsClient.Client.PollingDuration = pollingDuration - azureClient.ResourceGroupsClient = hashiGroupsSDK.NewResourceGroupsClientWithBaseURI(*resourceManagerEndpoint) + azureClient.ResourceGroupsClient = resourcegroups.NewResourceGroupsClientWithBaseURI(*resourceManagerEndpoint) azureClient.ResourceGroupsClient.Client.Authorizer = authWrapper.AutorestAuthorizer(resourceManagerAuthorizer) azureClient.ResourceGroupsClient.Client.RequestInspector = withInspection(maxlen) azureClient.ResourceGroupsClient.Client.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient)) azureClient.ResourceGroupsClient.Client.UserAgent = fmt.Sprintf("%s %s", useragent.String(version.AzurePluginVersion.FormattedVersion()), azureClient.ResourceGroupsClient.Client.UserAgent) azureClient.ResourceGroupsClient.Client.PollingDuration = pollingDuration - azureClient.ImagesClient = hashiImagesSDK.NewImagesClientWithBaseURI(*resourceManagerEndpoint) + azureClient.ImagesClient = images.NewImagesClientWithBaseURI(*resourceManagerEndpoint) azureClient.ImagesClient.Client.Authorizer = authWrapper.AutorestAuthorizer(resourceManagerAuthorizer) azureClient.ImagesClient.Client.RequestInspector = withInspection(maxlen) azureClient.ImagesClient.Client.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient)) azureClient.ImagesClient.Client.UserAgent = fmt.Sprintf("%s %s", useragent.String(version.AzurePluginVersion.FormattedVersion()), azureClient.ImagesClient.Client.UserAgent) azureClient.ImagesClient.Client.PollingDuration = pollingDuration - azureClient.StorageAccountsClient = hashiStorageAccountsSDK.NewStorageAccountsClientWithBaseURI(*resourceManagerEndpoint) + azureClient.StorageAccountsClient = storageaccounts.NewStorageAccountsClientWithBaseURI(*resourceManagerEndpoint) azureClient.StorageAccountsClient.Client.Authorizer = authWrapper.AutorestAuthorizer(resourceManagerAuthorizer) azureClient.StorageAccountsClient.Client.RequestInspector = withInspection(maxlen) azureClient.StorageAccountsClient.Client.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient)) @@ -184,7 +171,7 @@ func NewAzureClient(ctx context.Context, isVHDBuild bool, cloud *environments.En azureClient.StorageAccountsClient.Client.PollingDuration = pollingDuration // TODO Request/Response inpectors for Track 2 - networkMetaClient, err := hashiNetworkMetaSDK.NewClientWithBaseURI(cloud.ResourceManager, func(c *resourcemanager.Client) { + networkMetaClient, err := networks.NewClientWithBaseURI(cloud.ResourceManager, func(c *resourcemanager.Client) { c.Client.Authorizer = resourceManagerAuthorizer c.Client.UserAgent = "some-user-agent" }) @@ -194,14 +181,14 @@ func NewAzureClient(ctx context.Context, isVHDBuild bool, cloud *environments.En } azureClient.NetworkMetaClient = *networkMetaClient - azureClient.GalleryImageVersionsClient = hashiGalleryImageVersionsSDK.NewGalleryImageVersionsClientWithBaseURI(*resourceManagerEndpoint) + azureClient.GalleryImageVersionsClient = galleryimageversions.NewGalleryImageVersionsClientWithBaseURI(*resourceManagerEndpoint) azureClient.GalleryImageVersionsClient.Client.Authorizer = authWrapper.AutorestAuthorizer(resourceManagerAuthorizer) azureClient.GalleryImageVersionsClient.Client.RequestInspector = withInspection(maxlen) azureClient.GalleryImageVersionsClient.Client.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient)) azureClient.GalleryImageVersionsClient.Client.UserAgent = fmt.Sprintf("%s %s", useragent.String(version.AzurePluginVersion.FormattedVersion()), azureClient.GalleryImageVersionsClient.Client.UserAgent) azureClient.GalleryImageVersionsClient.Client.PollingDuration = sharedGalleryTimeout - azureClient.GalleryImagesClient = hashiGalleryImagesSDK.NewGalleryImagesClientWithBaseURI(*resourceManagerEndpoint) + azureClient.GalleryImagesClient = galleryimages.NewGalleryImagesClientWithBaseURI(*resourceManagerEndpoint) azureClient.GalleryImagesClient.Client.Authorizer = authWrapper.AutorestAuthorizer(resourceManagerAuthorizer) azureClient.GalleryImagesClient.Client.RequestInspector = withInspection(maxlen) azureClient.GalleryImagesClient.Client.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient)) @@ -210,7 +197,7 @@ func NewAzureClient(ctx context.Context, isVHDBuild bool, cloud *environments.En // We only need the Blob Client to delete the OS VHD during VHD builds if isVHDBuild { - storageAccountAuthorizer, err := buildStorageAuthorizer(ctx, newSdkAuthOptions, *cloud) + storageAccountAuthorizer, err := commonclient.BuildStorageAuthorizer(ctx, newSdkAuthOptions, *cloud) if err != nil { return nil, nil, err } @@ -227,7 +214,7 @@ func NewAzureClient(ctx context.Context, isVHDBuild bool, cloud *environments.En return nil, nil, err } // TODO Handle potential panic here if Access Token or child objects are null - objectId, err := getObjectIdFromToken(token.AccessToken) + objectId, err := commonclient.GetObjectIdFromToken(token.AccessToken) if err != nil { return nil, nil, err } @@ -251,91 +238,3 @@ func getInspectorMaxLength() int64 { return i } - -const ( - AuthTypeDeviceLogin = "DeviceLogin" - AuthTypeMSI = "ManagedIdentity" - AuthTypeClientSecret = "ClientSecret" - AuthTypeClientCert = "ClientCertificate" - AuthTypeClientBearerJWT = "ClientBearerJWT" - AuthTypeAzureCLI = "AzureCLI" -) - -func buildResourceManagerAuthorizer(ctx context.Context, authOpts NewSDKAuthOptions, env environments.Environment) (auth.Authorizer, error) { - authorizer, err := buildAuthorizer(ctx, authOpts, env, env.ResourceManager) - if err != nil { - return nil, fmt.Errorf("building Resource Manager authorizer from credentials: %+v", err) - } - return authorizer, nil -} - -func buildStorageAuthorizer(ctx context.Context, authOpts NewSDKAuthOptions, env environments.Environment) (auth.Authorizer, error) { - authorizer, err := buildAuthorizer(ctx, authOpts, env, env.Storage) - if err != nil { - return nil, fmt.Errorf("building Storage authorizer from credentials: %+v", err) - } - return authorizer, nil -} - -func buildAuthorizer(ctx context.Context, authOpts NewSDKAuthOptions, env environments.Environment, api environments.Api) (auth.Authorizer, error) { - var authConfig auth.Credentials - switch authOpts.AuthType { - case AuthTypeDeviceLogin: - return nil, fmt.Errorf("DeviceLogin is not supported in v2 of the Azure Packer Plugin, however you can use the Azure CLI `az login --use-device-code` to use a device code, and then use CLI authentication") - case AuthTypeAzureCLI: - authConfig = auth.Credentials{ - Environment: env, - EnableAuthenticatingUsingAzureCLI: true, - } - case AuthTypeMSI: - authConfig = auth.Credentials{ - Environment: env, - EnableAuthenticatingUsingManagedIdentity: true, - } - case AuthTypeClientSecret: - authConfig = auth.Credentials{ - Environment: env, - EnableAuthenticatingUsingClientSecret: true, - ClientID: authOpts.ClientID, - ClientSecret: authOpts.ClientSecret, - TenantID: authOpts.TenantID, - } - case AuthTypeClientCert: - authConfig = auth.Credentials{ - Environment: env, - EnableAuthenticatingUsingClientCertificate: true, - ClientID: authOpts.ClientID, - ClientCertificatePath: authOpts.ClientCertPath, - ClientCertificatePassword: "", - } - case AuthTypeClientBearerJWT: - authConfig = auth.Credentials{ - Environment: env, - EnableAuthenticationUsingOIDC: true, - ClientID: authOpts.ClientID, - TenantID: authOpts.TenantID, - OIDCAssertionToken: authOpts.ClientJWT, - } - default: - panic("AuthType not set") - } - authorizer, err := auth.NewAuthorizerFromCredentials(ctx, authConfig, api) - if err != nil { - return nil, err - } - return authorizer, nil -} - -func getObjectIdFromToken(token string) (string, error) { - claims := jwt.MapClaims{} - var p jwt.Parser - - var err error - - _, _, err = p.ParseUnverified(token, claims) - - if err != nil { - return "", err - } - return claims["oid"].(string), nil -} diff --git a/builder/azure/arm/builder.go b/builder/azure/arm/builder.go index 64a9d79a..12801ce3 100644 --- a/builder/azure/arm/builder.go +++ b/builder/azure/arm/builder.go @@ -14,14 +14,15 @@ import ( "strings" "time" - hashiImagesSDK "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-01/images" - hashiGalleryImagesSDK "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-03/galleryimages" - hashiGalleryImageVersionsSDK "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-03/galleryimageversions" - hashiStorageAccountsSDK "github.com/hashicorp/go-azure-sdk/resource-manager/storage/2022-09-01/storageaccounts" + "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-01/images" + "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-03/galleryimages" + "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-03/galleryimageversions" + "github.com/hashicorp/go-azure-sdk/resource-manager/storage/2022-09-01/storageaccounts" "github.com/hashicorp/go-azure-helpers/resourcemanager/commonids" "github.com/hashicorp/hcl/v2/hcldec" packerAzureCommon "github.com/hashicorp/packer-plugin-azure/builder/azure/common" + commonclient "github.com/hashicorp/packer-plugin-azure/builder/azure/common/client" "github.com/hashicorp/packer-plugin-azure/builder/azure/common/constants" "github.com/hashicorp/packer-plugin-azure/builder/azure/common/lin" "github.com/hashicorp/packer-plugin-sdk/communicator" @@ -90,7 +91,7 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) b.stateBag.Put(constants.Ui, ui) // Pass in relevant auth information for hashicorp/go-azure-sdk - authOptions := NewSDKAuthOptions{ + authOptions := commonclient.NewSDKAuthOptions{ AuthType: b.config.ClientConfig.AuthType(), ClientID: b.config.ClientConfig.ClientID, ClientSecret: b.config.ClientConfig.ClientSecret, @@ -104,7 +105,7 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) azureClient, objectID, err := NewAzureClient( ctx, (b.config.ResourceGroupName != "" || b.config.StorageAccount != ""), - b.config.ClientConfig.NewCloudEnvironment(), + b.config.ClientConfig.CloudEnvironment(), b.config.SharedGalleryTimeout, b.config.PollingDurationTimeout, authOptions, @@ -137,8 +138,8 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) } // If a managed image already exists it cannot be overwritten. - imageId := hashiImagesSDK.NewImageID(b.config.ClientConfig.SubscriptionID, b.config.ManagedImageResourceGroupName, b.config.ManagedImageName) - _, err = azureClient.ImagesClient.Get(ctx, imageId, hashiImagesSDK.DefaultGetOperationOptions()) + imageId := images.NewImageID(b.config.ClientConfig.SubscriptionID, b.config.ManagedImageResourceGroupName, b.config.ManagedImageName) + _, err = azureClient.ImagesClient.Get(ctx, imageId, images.DefaultGetOperationOptions()) 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)) @@ -198,15 +199,15 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) sigSubscriptionID = b.stateBag.Get(constants.ArmSubscription).(string) } b.stateBag.Put(constants.ArmSharedImageGalleryDestinationSubscription, sigSubscriptionID) - galleryId := hashiGalleryImagesSDK.NewGalleryImageID(sigSubscriptionID, b.config.SharedGalleryDestination.SigDestinationResourceGroup, b.config.SharedGalleryDestination.SigDestinationGalleryName, b.config.SharedGalleryDestination.SigDestinationImageName) + galleryId := galleryimages.NewGalleryImageID(sigSubscriptionID, b.config.SharedGalleryDestination.SigDestinationResourceGroup, b.config.SharedGalleryDestination.SigDestinationGalleryName, b.config.SharedGalleryDestination.SigDestinationImageName) _, err = azureClient.GalleryImagesClient.Get(ctx, galleryId) if err != nil { return nil, fmt.Errorf("the Shared Gallery Image '%s' to which to publish the managed image version to does not exist in the resource group '%s' or does not contain managed image '%s'", b.config.SharedGalleryDestination.SigDestinationGalleryName, b.config.SharedGalleryDestination.SigDestinationResourceGroup, b.config.SharedGalleryDestination.SigDestinationImageName) } // Check if a Image Version already exists for our target destination - galleryImageVersionId := hashiGalleryImageVersionsSDK.NewImageVersionID(sigSubscriptionID, b.config.SharedGalleryDestination.SigDestinationResourceGroup, b.config.SharedGalleryDestination.SigDestinationGalleryName, b.config.SharedGalleryDestination.SigDestinationImageName, b.config.SharedGalleryDestination.SigDestinationImageVersion) - _, err := azureClient.GalleryImageVersionsClient.Get(ctx, galleryImageVersionId, hashiGalleryImageVersionsSDK.DefaultGetOperationOptions()) + galleryImageVersionId := galleryimageversions.NewImageVersionID(sigSubscriptionID, b.config.SharedGalleryDestination.SigDestinationResourceGroup, b.config.SharedGalleryDestination.SigDestinationGalleryName, b.config.SharedGalleryDestination.SigDestinationImageName, b.config.SharedGalleryDestination.SigDestinationImageVersion) + _, err := azureClient.GalleryImageVersionsClient.Get(ctx, galleryImageVersionId, galleryimageversions.DefaultGetOperationOptions()) if err == nil { return nil, fmt.Errorf("a gallery image version for image name:version %s:%s already exists in gallery %s", b.config.SharedGalleryDestination.SigDestinationImageName, b.config.SharedGalleryDestination.SigDestinationImageVersion, b.config.SharedGalleryDestination.SigDestinationGalleryName) } @@ -232,7 +233,7 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) sourceImageSpecialized := false if b.config.SharedGallery.GalleryName != "" { client := azureClient.GalleryImagesClient - id := hashiGalleryImagesSDK.NewGalleryImageID(b.config.SharedGallery.Subscription, b.config.SharedGallery.ResourceGroup, b.config.SharedGallery.GalleryName, b.config.SharedGallery.ImageName) + id := galleryimages.NewGalleryImageID(b.config.SharedGallery.Subscription, b.config.SharedGallery.ResourceGroup, b.config.SharedGallery.GalleryName, b.config.SharedGallery.ImageName) galleryImage, err := client.Get(ctx, id) if err != nil { return nil, fmt.Errorf("the parent Shared Gallery Image '%s' from which to source the managed image version to does not exist in the resource group '%s' or does not contain managed image '%s'", b.config.SharedGallery.GalleryName, b.config.SharedGallery.ResourceGroup, b.config.SharedGallery.ImageName) @@ -240,7 +241,7 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) if galleryImage.Model == nil { return nil, fmt.Errorf("SDK returned empty model for gallery image") } - if galleryImage.Model.Properties.OsState == hashiGalleryImagesSDK.OperatingSystemStateTypesSpecialized { + if galleryImage.Model.Properties.OsState == galleryimages.OperatingSystemStateTypesSpecialized { sourceImageSpecialized = true } } @@ -449,7 +450,7 @@ func (b *Builder) writeSSHPrivateKey(ui packersdk.Ui, debugKeyPath string) { } func (b *Builder) isPublicPrivateNetworkCommunication() bool { - return DefaultPrivateVirtualNetworkWithPublicIp != b.config.PrivateVirtualNetworkWithPublicIp + return b.config.PrivateVirtualNetworkWithPublicIp } func (b *Builder) isPrivateNetworkCommunication() bool { @@ -464,9 +465,9 @@ func canonicalizeLocation(location string) string { return strings.Replace(location, " ", "", -1) } -func (b *Builder) getBlobAccount(ctx context.Context, client *AzureClient, subscriptionId string, resourceGroupName string, storageAccountName string) (*hashiStorageAccountsSDK.StorageAccount, error) { - id := hashiStorageAccountsSDK.NewStorageAccountID(subscriptionId, resourceGroupName, storageAccountName) - account, err := client.StorageAccountsClient.GetProperties(ctx, id, hashiStorageAccountsSDK.DefaultGetPropertiesOperationOptions()) +func (b *Builder) getBlobAccount(ctx context.Context, client *AzureClient, subscriptionId string, resourceGroupName string, storageAccountName string) (*storageaccounts.StorageAccount, error) { + id := storageaccounts.NewStorageAccountID(subscriptionId, resourceGroupName, storageAccountName) + account, err := client.StorageAccountsClient.GetProperties(ctx, id, storageaccounts.DefaultGetPropertiesOperationOptions()) if err != nil { return nil, err } diff --git a/builder/azure/arm/builder_acc_test.go b/builder/azure/arm/builder_acc_test.go index f7d557a3..95aea30b 100644 --- a/builder/azure/arm/builder_acc_test.go +++ b/builder/azure/arm/builder_acc_test.go @@ -37,8 +37,9 @@ import ( "testing" "time" - hashiGalleryImagesSDK "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-03/galleryimages" + "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-03/galleryimages" "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-03/galleryimageversions" + commonclient "github.com/hashicorp/packer-plugin-azure/builder/azure/common/client" "github.com/hashicorp/packer-plugin-sdk/acctest" packersdk "github.com/hashicorp/packer-plugin-sdk/packer" "github.com/hashicorp/packer-plugin-sdk/retry" @@ -217,27 +218,6 @@ func TestBuilderAcc_ManagedDisk_Windows_Build_Resource_Group_Additional_Disk(t * }) } -func TestBuilderAcc_ManagedDisk_Windows_DeviceLogin(t *testing.T) { - t.Parallel() - if os.Getenv(DeviceLoginAcceptanceTest) == "" { - t.Skipf("Device Login Acceptance tests skipped unless env '%s' set, as its requires manual step during execution", DeviceLoginAcceptanceTest) - return - } - acctest.TestPlugin(t, &acctest.PluginTestCase{ - Name: "test-azure-managedisk-windows-devicelogin", - Type: "azure-arm", - Template: testBuilderAccManagedDiskWindowsDeviceLogin, - Check: func(buildCommand *exec.Cmd, logfile string) error { - if buildCommand.ProcessState != nil { - if buildCommand.ProcessState.ExitCode() != 0 { - return fmt.Errorf("Bad exit code. Logfile: %s", logfile) - } - } - return nil - }, - }) -} - func TestBuilderAcc_ManagedDisk_Linux(t *testing.T) { t.Parallel() acctest.TestPlugin(t, &acctest.PluginTestCase{ @@ -255,27 +235,6 @@ func TestBuilderAcc_ManagedDisk_Linux(t *testing.T) { }) } -func TestBuilderAcc_ManagedDisk_Linux_DeviceLogin(t *testing.T) { - t.Parallel() - if os.Getenv(DeviceLoginAcceptanceTest) == "" { - t.Skipf("Device Login Acceptance tests skipped unless env '%s' set, as its requires manual step during execution", DeviceLoginAcceptanceTest) - return - } - acctest.TestPlugin(t, &acctest.PluginTestCase{ - Name: "test-azure-managedisk-linux-device-login", - Type: "azure-arm", - Template: testBuilderAccManagedDiskLinuxDeviceLogin, - Check: func(buildCommand *exec.Cmd, logfile string) error { - if buildCommand.ProcessState != nil { - if buildCommand.ProcessState.ExitCode() != 0 { - return fmt.Errorf("Bad exit code. Logfile: %s", logfile) - } - } - return nil - }, - }) -} - func TestBuilderAcc_ManagedDisk_Linux_AzureCLI(t *testing.T) { t.Parallel() if os.Getenv("PACKER_ACC") == "" { @@ -415,7 +374,7 @@ func createTestAzureClient(t *testing.T) AzureClient { // Use CLI auth for our test client b.config.ClientConfig.UseAzureCLIAuth = true _ = b.config.ClientConfig.FillParameters() - authOptions := NewSDKAuthOptions{ + authOptions := commonclient.NewSDKAuthOptions{ AuthType: b.config.ClientConfig.AuthType(), ClientID: b.config.ClientConfig.ClientID, ClientSecret: b.config.ClientConfig.ClientSecret, @@ -426,7 +385,7 @@ func createTestAzureClient(t *testing.T) AzureClient { azureClient, _, err := NewAzureClient( context.TODO(), true, - b.config.ClientConfig.NewCloudEnvironment(), + b.config.ClientConfig.CloudEnvironment(), b.config.SharedGalleryTimeout, b.config.PollingDurationTimeout, authOptions) @@ -438,31 +397,31 @@ func createTestAzureClient(t *testing.T) AzureClient { func createSharedImageGalleryDefinition(t *testing.T, params CreateSharedImageGalleryDefinitionParameters) { azureClient := createTestAzureClient(t) - osType := hashiGalleryImagesSDK.OperatingSystemTypesLinux + osType := galleryimages.OperatingSystemTypesLinux if params.isWindows { - osType = hashiGalleryImagesSDK.OperatingSystemTypesWindows + osType = galleryimages.OperatingSystemTypesWindows } - osState := hashiGalleryImagesSDK.OperatingSystemStateTypesGeneralized + osState := galleryimages.OperatingSystemStateTypesGeneralized if params.specialized { - osState = hashiGalleryImagesSDK.OperatingSystemStateTypesSpecialized + osState = galleryimages.OperatingSystemStateTypesSpecialized } - osArch := hashiGalleryImagesSDK.ArchitectureArmSixFour + osArch := galleryimages.ArchitectureArmSixFour if params.isX64 { - osArch = hashiGalleryImagesSDK.ArchitectureXSixFour + osArch = galleryimages.ArchitectureXSixFour } - hyperVGeneration := hashiGalleryImagesSDK.HyperVGenerationVOne + hyperVGeneration := galleryimages.HyperVGenerationVOne if params.useGenTwoVM { - hyperVGeneration = hashiGalleryImagesSDK.HyperVGenerationVTwo + hyperVGeneration = galleryimages.HyperVGenerationVTwo } location := "southcentralus" - galleryId := hashiGalleryImagesSDK.NewGalleryImageID(params.subscriptionId, "packer-acceptance-test", "acctestgallery", params.galleryImageName) - _, err := azureClient.GalleryImagesClient.CreateOrUpdate(context.TODO(), galleryId, hashiGalleryImagesSDK.GalleryImage{ - Properties: &hashiGalleryImagesSDK.GalleryImageProperties{ + galleryId := galleryimages.NewGalleryImageID(params.subscriptionId, "packer-acceptance-test", "acctestgallery", params.galleryImageName) + _, err := azureClient.GalleryImagesClient.CreateOrUpdate(context.TODO(), galleryId, galleryimages.GalleryImage{ + Properties: &galleryimages.GalleryImageProperties{ OsType: osType, OsState: osState, Architecture: &osArch, HyperVGeneration: &hyperVGeneration, - Identifier: hashiGalleryImagesSDK.GalleryImageIdentifier{ + Identifier: galleryimages.GalleryImageIdentifier{ Publisher: params.imagePublisher, Offer: params.imageOffer, Sku: params.imageSku, @@ -493,7 +452,7 @@ func deleteSharedImageGalleryDefinition(t *testing.T, subscriptionID string, gal RetryDelay: (&retry.Backoff{InitialBackoff: 2 * time.Second, MaxBackoff: 30 * time.Second, Multiplier: 2}).Linear, } err := retryConfig.Run(context.TODO(), func(ctx context.Context) error { - id := hashiGalleryImagesSDK.NewGalleryImageID(subscriptionID, "packer-acceptance-test", "acctestgallery", galleryImageName) + id := galleryimages.NewGalleryImageID(subscriptionID, "packer-acceptance-test", "acctestgallery", galleryImageName) err := azureClient.GalleryImagesClient.DeleteThenPoll(context.TODO(), id) if err != nil { return err @@ -658,36 +617,6 @@ const testBuilderAccManagedDiskWindowsBuildResourceGroupAdditionalDisk = ` } ` -const testBuilderAccManagedDiskWindowsDeviceLogin = ` -{ - "variables": { - "subscription_id": "{{env ` + "`ARM_SUBSCRIPTION_ID`" + `}}" - }, - "builders": [{ - "type": "azure-arm", - - "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": { @@ -720,31 +649,6 @@ const testBuilderAccManagedDiskLinux = ` } ` -const testBuilderAccManagedDiskLinuxDeviceLogin = ` -{ - "variables": { - "subscription_id": "{{env ` + "`ARM_SUBSCRIPTION_ID`" + `}}" - }, - "builders": [{ - "type": "azure-arm", - - "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": { diff --git a/builder/azure/arm/config.go b/builder/azure/arm/config.go index d19acbc6..f1c08ab8 100644 --- a/builder/azure/arm/config.go +++ b/builder/azure/arm/config.go @@ -23,8 +23,8 @@ import ( "github.com/hashicorp/packer-plugin-sdk/random" - hashiImagesSDK "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-01/images" - hashiVMSDK "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-01/virtualmachines" + "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-01/images" + "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-01/virtualmachines" "github.com/masterzen/winrm" azcommon "github.com/hashicorp/packer-plugin-azure/builder/azure/common" @@ -112,7 +112,7 @@ type SharedImageGalleryDestination struct { type Spot struct { // Specify eviction policy for spot instance: "Deallocate" or "Delete". If this is set, a spot instance will be used. - EvictionPolicy hashiVMSDK.VirtualMachineEvictionPolicyTypes `mapstructure:"eviction_policy"` + EvictionPolicy virtualmachines.VirtualMachineEvictionPolicyTypes `mapstructure:"eviction_policy"` // How much should the VM cost maximally per hour. Specify -1 (or do not specify) to not evict based on price. MaxPrice float32 `mapstructure:"max_price"` } @@ -316,7 +316,7 @@ type Config struct { // type for a managed image. Valid values are Standard_LRS and Premium_LRS. // The default is Standard_LRS. ManagedImageStorageAccountType string `mapstructure:"managed_image_storage_account_type" required:"false"` - managedImageStorageAccountType hashiVMSDK.StorageAccountTypes + managedImageStorageAccountType virtualmachines.StorageAccountTypes // 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. @@ -483,7 +483,7 @@ type Config struct { // PlanInfo PlanInformation `mapstructure:"plan_info" required:"false"` // 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) + // that value. // 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 @@ -524,7 +524,7 @@ type Config struct { // 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 hashiVMSDK.CachingTypes + diskCachingType virtualmachines.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 @@ -639,21 +639,21 @@ func (c *Config) isPublishToSIG() bool { return c.SharedGalleryDestination.SigDestinationGalleryName != "" } -func (c *Config) toVirtualMachineCaptureParameters() *hashiVMSDK.VirtualMachineCaptureParameters { - return &hashiVMSDK.VirtualMachineCaptureParameters{ +func (c *Config) toVirtualMachineCaptureParameters() *virtualmachines.VirtualMachineCaptureParameters { + return &virtualmachines.VirtualMachineCaptureParameters{ DestinationContainerName: c.CaptureContainerName, VhdPrefix: c.CaptureNamePrefix, OverwriteVhds: false, } } -func (c *Config) toImageParameters() *hashiImagesSDK.Image { - return &hashiImagesSDK.Image{ - Properties: &hashiImagesSDK.ImageProperties{ - SourceVirtualMachine: &hashiImagesSDK.SubResource{ +func (c *Config) toImageParameters() *images.Image { + return &images.Image{ + Properties: &images.ImageProperties{ + SourceVirtualMachine: &images.SubResource{ Id: azcommon.StringPtr(c.toVMID()), }, - StorageProfile: &hashiImagesSDK.ImageStorageProfile{ + StorageProfile: &images.ImageStorageProfile{ ZoneResilient: azcommon.BoolPtr(c.ManagedImageZoneResilient), }, }, @@ -984,11 +984,11 @@ func provideDefaultValues(c *Config) { } if c.ManagedImageStorageAccountType == "" { - c.managedImageStorageAccountType = hashiVMSDK.StorageAccountTypesStandardLRS + c.managedImageStorageAccountType = virtualmachines.StorageAccountTypesStandardLRS } if c.DiskCachingType == "" { - c.diskCachingType = hashiVMSDK.CachingTypesReadWrite + c.diskCachingType = virtualmachines.CachingTypesReadWrite } if c.ImagePublisher != "" && c.ImageVersion == "" { @@ -1331,22 +1331,22 @@ func assertRequiredParametersSet(c *Config, errs *packersdk.MultiError) { ///////////////////////////////////////////// // Storage if c.Spot.EvictionPolicy != "" { - if c.Spot.EvictionPolicy != hashiVMSDK.VirtualMachineEvictionPolicyTypesDelete && c.Spot.EvictionPolicy != hashiVMSDK.VirtualMachineEvictionPolicyTypesDeallocate { - errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("The spot.eviction_policy %q is invalid, eviction_policy must be %q, %q, or unset", c.Spot.EvictionPolicy, hashiVMSDK.VirtualMachineEvictionPolicyTypesDelete, hashiVMSDK.VirtualMachineEvictionPolicyTypesDeallocate)) + if c.Spot.EvictionPolicy != virtualmachines.VirtualMachineEvictionPolicyTypesDelete && c.Spot.EvictionPolicy != virtualmachines.VirtualMachineEvictionPolicyTypesDeallocate { + errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("The spot.eviction_policy %q is invalid, eviction_policy must be %q, %q, or unset", c.Spot.EvictionPolicy, virtualmachines.VirtualMachineEvictionPolicyTypesDelete, virtualmachines.VirtualMachineEvictionPolicyTypesDeallocate)) } } else { if c.Spot.MaxPrice != 0 { - errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("Setting a spot.max_price without an spot.eviction_policy is invalid, eviction_policy must be %q or %q if max_price is set", hashiVMSDK.VirtualMachineEvictionPolicyTypesDelete, hashiVMSDK.VirtualMachineEvictionPolicyTypesDeallocate)) + errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("Setting a spot.max_price without an spot.eviction_policy is invalid, eviction_policy must be %q or %q if max_price is set", virtualmachines.VirtualMachineEvictionPolicyTypesDelete, virtualmachines.VirtualMachineEvictionPolicyTypesDeallocate)) } } ///////////////////////////////////////////// // Storage switch c.ManagedImageStorageAccountType { - case "", string(hashiVMSDK.StorageAccountTypesStandardLRS): - c.managedImageStorageAccountType = hashiVMSDK.StorageAccountTypesStandardLRS - case string(hashiVMSDK.StorageAccountTypesPremiumLRS): - c.managedImageStorageAccountType = hashiVMSDK.StorageAccountTypesPremiumLRS + case "", string(virtualmachines.StorageAccountTypesStandardLRS): + c.managedImageStorageAccountType = virtualmachines.StorageAccountTypesStandardLRS + case string(virtualmachines.StorageAccountTypesPremiumLRS): + c.managedImageStorageAccountType = virtualmachines.StorageAccountTypesPremiumLRS default: errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("The managed_image_storage_account_type %q is invalid", c.ManagedImageStorageAccountType)) } @@ -1356,12 +1356,12 @@ func assertRequiredParametersSet(c *Config, errs *packersdk.MultiError) { } switch c.DiskCachingType { - case string(hashiVMSDK.CachingTypesNone): - c.diskCachingType = hashiVMSDK.CachingTypesNone - case string(hashiVMSDK.CachingTypesReadOnly): - c.diskCachingType = hashiVMSDK.CachingTypesReadOnly - case "", string(hashiVMSDK.CachingTypesReadWrite): - c.diskCachingType = hashiVMSDK.CachingTypesReadWrite + case string(virtualmachines.CachingTypesNone): + c.diskCachingType = virtualmachines.CachingTypesNone + case string(virtualmachines.CachingTypesReadOnly): + c.diskCachingType = virtualmachines.CachingTypesReadOnly + case "", string(virtualmachines.CachingTypesReadWrite): + c.diskCachingType = virtualmachines.CachingTypesReadWrite default: errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("The disk_caching_type %q is invalid", c.DiskCachingType)) } diff --git a/builder/azure/arm/config.hcl2spec.go b/builder/azure/arm/config.hcl2spec.go index 091786a2..40376899 100644 --- a/builder/azure/arm/config.hcl2spec.go +++ b/builder/azure/arm/config.hcl2spec.go @@ -32,7 +32,6 @@ type FlatConfig struct { 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"` - UseInteractiveAuth *bool `mapstructure:"use_interactive_auth" required:"false" cty:"use_interactive_auth" hcl:"use_interactive_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"` @@ -177,7 +176,6 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "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}, - "use_interactive_auth": &hcldec.AttrSpec{Name: "use_interactive_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}, diff --git a/builder/azure/arm/config_test.go b/builder/azure/arm/config_test.go index 3ca912d6..b4e96fc7 100644 --- a/builder/azure/arm/config_test.go +++ b/builder/azure/arm/config_test.go @@ -12,7 +12,7 @@ import ( "time" "github.com/google/go-cmp/cmp" - hashiVMSDK "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-01/virtualmachines" + "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-01/virtualmachines" "github.com/hashicorp/packer-plugin-azure/builder/azure/common/constants" sdkconfig "github.com/hashicorp/packer-plugin-sdk/template/config" ) @@ -128,11 +128,11 @@ func TestConfigShouldBeAbleToOverrideDefaultedValues(t *testing.T) { t.Errorf("Expected 'vm_size' to be set to 'override_vm_size', but found %q!", c.VMSize) } - if c.managedImageStorageAccountType != hashiVMSDK.StorageAccountTypesPremiumLRS { + if c.managedImageStorageAccountType != virtualmachines.StorageAccountTypesPremiumLRS { t.Errorf("Expected 'managed_image_storage_account_type' to be set to 'Premium_LRS', but found %q!", c.managedImageStorageAccountType) } - if c.diskCachingType != hashiVMSDK.CachingTypesNone { + if c.diskCachingType != virtualmachines.CachingTypesNone { t.Errorf("Expected 'disk_caching_type' to be set to 'None', but found %q!", c.diskCachingType) } @@ -505,8 +505,8 @@ func TestConfigShouldDefaultToPublicCloud(t *testing.T) { 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()) + if c.ClientConfig.CloudEnvironment() == nil || c.ClientConfig.CloudEnvironment().Name != "Public" { + t.Errorf("Expected 'cloudEnvironment' to be set to 'Public', but got '%s'.", c.ClientConfig.CloudEnvironment().Name) } } @@ -525,25 +525,25 @@ func TestConfigInstantiatesCorrectAzureEnvironment(t *testing.T) { "communicator": "none", } - // user input is fun :) + // user input is fun :D var table = []struct { name string environmentName string }{ - {"China", "AzureChinaCloud"}, - {"ChinaCloud", "AzureChinaCloud"}, - {"AzureChinaCloud", "AzureChinaCloud"}, - {"aZuReChInAcLoUd", "AzureChinaCloud"}, + {"China", "China"}, + {"ChinaCloud", "China"}, + {"AzureChinaCloud", "China"}, + {"aZuReChInAcLoUd", "China"}, - {"USGovernment", "AzureUSGovernmentCloud"}, - {"USGovernmentCloud", "AzureUSGovernmentCloud"}, - {"AzureUSGovernmentCloud", "AzureUSGovernmentCloud"}, - {"aZuReUsGoVeRnMeNtClOuD", "AzureUSGovernmentCloud"}, + {"USGovernment", "USGovernment"}, + {"USGovernmentCloud", "USGovernment"}, + {"AzureUSGovernmentCloud", "USGovernment"}, + {"aZuReUsGoVeRnMeNtClOuD", "USGovernment"}, - {"Public", "AzurePublicCloud"}, - {"PublicCloud", "AzurePublicCloud"}, - {"AzurePublicCloud", "AzurePublicCloud"}, - {"aZuRePuBlIcClOuD", "AzurePublicCloud"}, + {"Public", "Public"}, + {"PublicCloud", "Public"}, + {"AzurePublicCloud", "Public"}, + {"aZuRePuBlIcClOuD", "Public"}, } packerConfiguration := getPackerConfiguration() @@ -557,7 +557,7 @@ func TestConfigInstantiatesCorrectAzureEnvironment(t *testing.T) { } 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()) + t.Errorf("Expected 'cloudEnvironment' to be set to '%s', but got '%s'.", x.environmentName, c.ClientConfig.CloudEnvironment().Name) } } } @@ -2367,7 +2367,7 @@ func getArmBuilderConfigurationWithWindows() map[string]string { m["object_id"] = "ignored00" m["tenant_id"] = "ignored00" m["subscription_id"] = "ignored00" - m["use_interactive_auth"] = "true" + m["use_azure_cli_auth"] = "true" m["winrm_username"] = "ignored00" m["communicator"] = "winrm" m["os_type"] = constants.Target_Windows diff --git a/builder/azure/arm/resource_resolver.go b/builder/azure/arm/resource_resolver.go index cd7f2f54..aab1cb7c 100644 --- a/builder/azure/arm/resource_resolver.go +++ b/builder/azure/arm/resource_resolver.go @@ -16,8 +16,8 @@ import ( "strings" "github.com/hashicorp/go-azure-helpers/resourcemanager/commonids" - hashiImagesSDK "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-01/images" - hashiSubnetsSDK "github.com/hashicorp/go-azure-sdk/resource-manager/network/2022-09-01/subnets" + "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-01/images" + "github.com/hashicorp/go-azure-sdk/resource-manager/network/2022-09-01/subnets" ) type resourceResolver struct { @@ -76,7 +76,7 @@ func getResourceGroupNameFromId(id string) string { return xs[4] } -func findManagedImageByName(client *AzureClient, name, subscriptionId, resourceGroupName string) (*hashiImagesSDK.Image, error) { +func findManagedImageByName(client *AzureClient, name, subscriptionId, resourceGroupName string) (*images.Image, error) { id := commonids.NewResourceGroupID(subscriptionId, resourceGroupName) images, err := client.ImagesClient.ListByResourceGroupComplete(context.TODO(), id) if err != nil { @@ -119,7 +119,7 @@ func findVirtualNetworkResourceGroup(client *AzureClient, subscriptionId, name s func findVirtualNetworkSubnet(client *AzureClient, subscriptionId string, resourceGroupName string, name string) (string, error) { - subnets, err := client.NetworkMetaClient.Subnets.List(context.TODO(), hashiSubnetsSDK.NewVirtualNetworkID(subscriptionId, resourceGroupName, name)) + subnets, err := client.NetworkMetaClient.Subnets.List(context.TODO(), subnets.NewVirtualNetworkID(subscriptionId, resourceGroupName, name)) if err != nil { return "", err } diff --git a/builder/azure/arm/step_capture_image.go b/builder/azure/arm/step_capture_image.go index b04565e3..f7203e89 100644 --- a/builder/azure/arm/step_capture_image.go +++ b/builder/azure/arm/step_capture_image.go @@ -7,8 +7,8 @@ import ( "context" "fmt" - hashiImagesSDK "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-01/images" - hashiVMSDK "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-01/virtualmachines" + "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-01/images" + "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-01/virtualmachines" "github.com/hashicorp/packer-plugin-azure/builder/azure/common/constants" "github.com/hashicorp/packer-plugin-sdk/multistep" packersdk "github.com/hashicorp/packer-plugin-sdk/packer" @@ -16,10 +16,10 @@ import ( type StepCaptureImage struct { client *AzureClient - generalizeVM func(ctx context.Context, vmId hashiVMSDK.VirtualMachineId) error - getVMInternalID func(ctx context.Context, vmId hashiVMSDK.VirtualMachineId) (string, error) - captureVhd func(ctx context.Context, vmId hashiVMSDK.VirtualMachineId, parameters *hashiVMSDK.VirtualMachineCaptureParameters) error - captureManagedImage func(ctx context.Context, subscriptionId string, resourceGroupName string, imageName string, parameters *hashiImagesSDK.Image) error + generalizeVM func(ctx context.Context, vmId virtualmachines.VirtualMachineId) error + getVMInternalID func(ctx context.Context, vmId virtualmachines.VirtualMachineId) (string, error) + captureVhd func(ctx context.Context, vmId virtualmachines.VirtualMachineId, parameters *virtualmachines.VirtualMachineCaptureParameters) error + captureManagedImage func(ctx context.Context, subscriptionId string, resourceGroupName string, imageName string, parameters *images.Image) error say func(message string) error func(e error) } @@ -42,7 +42,7 @@ func NewStepCaptureImage(client *AzureClient, ui packersdk.Ui) *StepCaptureImage return step } -func (s *StepCaptureImage) generalize(ctx context.Context, vmId hashiVMSDK.VirtualMachineId) error { +func (s *StepCaptureImage) generalize(ctx context.Context, vmId virtualmachines.VirtualMachineId) error { _, err := s.client.Generalize(ctx, vmId) if err != nil { s.say(s.client.LastError.Error()) @@ -50,8 +50,8 @@ func (s *StepCaptureImage) generalize(ctx context.Context, vmId hashiVMSDK.Virtu return err } -func (s *StepCaptureImage) captureImageFromVM(ctx context.Context, subscriptionId string, resourceGroupName string, imageName string, image *hashiImagesSDK.Image) error { - id := hashiImagesSDK.NewImageID(subscriptionId, resourceGroupName, imageName) +func (s *StepCaptureImage) captureImageFromVM(ctx context.Context, subscriptionId string, resourceGroupName string, imageName string, image *images.Image) error { + id := images.NewImageID(subscriptionId, resourceGroupName, imageName) err := s.client.ImagesClient.CreateOrUpdateThenPoll(ctx, id, *image) if err != nil { s.say(s.client.LastError.Error()) @@ -59,7 +59,7 @@ func (s *StepCaptureImage) captureImageFromVM(ctx context.Context, subscriptionI return err } -func (s *StepCaptureImage) captureImage(ctx context.Context, vmId hashiVMSDK.VirtualMachineId, parameters *hashiVMSDK.VirtualMachineCaptureParameters) error { +func (s *StepCaptureImage) captureImage(ctx context.Context, vmId virtualmachines.VirtualMachineId, parameters *virtualmachines.VirtualMachineCaptureParameters) error { if err := s.client.VirtualMachinesClient.CaptureThenPoll(ctx, vmId, *parameters); err != nil { s.say(s.client.LastError.Error()) return err @@ -67,8 +67,8 @@ func (s *StepCaptureImage) captureImage(ctx context.Context, vmId hashiVMSDK.Vir return nil } -func (s *StepCaptureImage) getVMID(ctx context.Context, vmId hashiVMSDK.VirtualMachineId) (string, error) { - vmResponse, err := s.client.VirtualMachinesClient.Get(ctx, vmId, hashiVMSDK.DefaultGetOperationOptions()) +func (s *StepCaptureImage) getVMID(ctx context.Context, vmId virtualmachines.VirtualMachineId) (string, error) { + vmResponse, err := s.client.VirtualMachinesClient.Get(ctx, vmId, virtualmachines.DefaultGetOperationOptions()) if err != nil { return "", err } @@ -84,14 +84,14 @@ func (s *StepCaptureImage) Run(ctx context.Context, state multistep.StateBag) mu 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.ArmNewVirtualMachineCaptureParameters).(*hashiVMSDK.VirtualMachineCaptureParameters) - var imageParameters = state.Get(constants.ArmImageParameters).(*hashiImagesSDK.Image) + var vmCaptureParameters = state.Get(constants.ArmNewVirtualMachineCaptureParameters).(*virtualmachines.VirtualMachineCaptureParameters) + var imageParameters = state.Get(constants.ArmImageParameters).(*images.Image) var subscriptionId = state.Get(constants.ArmSubscription).(string) var isManagedImage = state.Get(constants.ArmIsManagedImage).(bool) var isSIGImage = state.Get(constants.ArmIsSIGImage).(bool) var skipGeneralization = state.Get(constants.ArmSharedImageGalleryDestinationSpecialized).(bool) - vmId := hashiVMSDK.NewVirtualMachineID(subscriptionId, resourceGroupName, computeName) + vmId := virtualmachines.NewVirtualMachineID(subscriptionId, resourceGroupName, computeName) s.say(fmt.Sprintf(" -> Compute ResourceGroupName : '%s'", resourceGroupName)) s.say(fmt.Sprintf(" -> Compute Name : '%s'", computeName)) s.say(fmt.Sprintf(" -> Compute Location : '%s'", location)) diff --git a/builder/azure/arm/step_capture_image_test.go b/builder/azure/arm/step_capture_image_test.go index 86bb739c..28ab8024 100644 --- a/builder/azure/arm/step_capture_image_test.go +++ b/builder/azure/arm/step_capture_image_test.go @@ -8,21 +8,21 @@ import ( "fmt" "testing" - hashiImagesSDK "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-01/images" - hashiVMSDK "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-01/virtualmachines" + "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-01/images" + "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-01/virtualmachines" "github.com/hashicorp/packer-plugin-azure/builder/azure/common/constants" "github.com/hashicorp/packer-plugin-sdk/multistep" ) func TestStepCaptureImageShouldFailIfCaptureFails(t *testing.T) { var testSubject = &StepCaptureImage{ - captureVhd: func(context.Context, hashiVMSDK.VirtualMachineId, *hashiVMSDK.VirtualMachineCaptureParameters) error { + captureVhd: func(context.Context, virtualmachines.VirtualMachineId, *virtualmachines.VirtualMachineCaptureParameters) error { return fmt.Errorf("!! Unit Test FAIL !!") }, - getVMInternalID: func(context.Context, hashiVMSDK.VirtualMachineId) (string, error) { + getVMInternalID: func(context.Context, virtualmachines.VirtualMachineId) (string, error) { return "id", nil }, - generalizeVM: func(context.Context, hashiVMSDK.VirtualMachineId) error { + generalizeVM: func(context.Context, virtualmachines.VirtualMachineId) error { return nil }, say: func(message string) {}, @@ -43,13 +43,13 @@ func TestStepCaptureImageShouldFailIfCaptureFails(t *testing.T) { func TestStepCaptureImageShouldFailIfGetVMIDFails(t *testing.T) { var testSubject = &StepCaptureImage{ - captureVhd: func(context.Context, hashiVMSDK.VirtualMachineId, *hashiVMSDK.VirtualMachineCaptureParameters) error { + captureVhd: func(context.Context, virtualmachines.VirtualMachineId, *virtualmachines.VirtualMachineCaptureParameters) error { return nil }, - getVMInternalID: func(context.Context, hashiVMSDK.VirtualMachineId) (string, error) { + getVMInternalID: func(context.Context, virtualmachines.VirtualMachineId) (string, error) { return "", fmt.Errorf("!! Unit Test FAIL !!") }, - generalizeVM: func(context.Context, hashiVMSDK.VirtualMachineId) error { + generalizeVM: func(context.Context, virtualmachines.VirtualMachineId) error { return nil }, say: func(message string) {}, @@ -69,13 +69,13 @@ func TestStepCaptureImageShouldFailIfGetVMIDFails(t *testing.T) { } func TestStepCaptureImageShouldPassIfCapturePasses(t *testing.T) { var testSubject = &StepCaptureImage{ - captureVhd: func(ctx context.Context, vmId hashiVMSDK.VirtualMachineId, parameters *hashiVMSDK.VirtualMachineCaptureParameters) error { + captureVhd: func(ctx context.Context, vmId virtualmachines.VirtualMachineId, parameters *virtualmachines.VirtualMachineCaptureParameters) error { return nil }, - generalizeVM: func(context.Context, hashiVMSDK.VirtualMachineId) error { + generalizeVM: func(context.Context, virtualmachines.VirtualMachineId) error { return nil }, - getVMInternalID: func(ctx context.Context, vmId hashiVMSDK.VirtualMachineId) (string, error) { + getVMInternalID: func(ctx context.Context, vmId virtualmachines.VirtualMachineId) (string, error) { return "id", nil }, say: func(message string) {}, @@ -97,10 +97,10 @@ func TestStepCaptureImageShouldPassIfCapturePasses(t *testing.T) { func TestStepCaptureImageShouldCallGeneralizeIfSpecializedIsFalse(t *testing.T) { generalizeCount := 0 var testSubject = &StepCaptureImage{ - captureVhd: func(context.Context, hashiVMSDK.VirtualMachineId, *hashiVMSDK.VirtualMachineCaptureParameters) error { + captureVhd: func(context.Context, virtualmachines.VirtualMachineId, *virtualmachines.VirtualMachineCaptureParameters) error { return nil }, - generalizeVM: func(context.Context, hashiVMSDK.VirtualMachineId) error { + generalizeVM: func(context.Context, virtualmachines.VirtualMachineId) error { generalizeCount++ return nil }, @@ -127,10 +127,10 @@ func TestStepCaptureImageShouldCallGeneralizeIfSpecializedIsFalse(t *testing.T) func TestStepCaptureImageShouldNotCallGeneralizeIfSpecializedIsTrue(t *testing.T) { generalizeCount := 0 var testSubject = &StepCaptureImage{ - captureVhd: func(context.Context, hashiVMSDK.VirtualMachineId, *hashiVMSDK.VirtualMachineCaptureParameters) error { + captureVhd: func(context.Context, virtualmachines.VirtualMachineId, *virtualmachines.VirtualMachineCaptureParameters) error { return nil }, - generalizeVM: func(context.Context, hashiVMSDK.VirtualMachineId) error { + generalizeVM: func(context.Context, virtualmachines.VirtualMachineId) error { generalizeCount++ return nil }, @@ -159,20 +159,20 @@ func TestStepCaptureImageShouldTakeStepArgumentsFromStateBag(t *testing.T) { var actualResourceGroupName string var actualComputeName string - var actualVirtualMachineCaptureParameters *hashiVMSDK.VirtualMachineCaptureParameters + var actualVirtualMachineCaptureParameters *virtualmachines.VirtualMachineCaptureParameters expectedVirtualMachineID := "id" var testSubject = &StepCaptureImage{ - captureVhd: func(ctx context.Context, id hashiVMSDK.VirtualMachineId, parameters *hashiVMSDK.VirtualMachineCaptureParameters) error { + captureVhd: func(ctx context.Context, id virtualmachines.VirtualMachineId, parameters *virtualmachines.VirtualMachineCaptureParameters) error { actualResourceGroupName = id.ResourceGroupName actualComputeName = id.VirtualMachineName actualVirtualMachineCaptureParameters = parameters return nil }, - getVMInternalID: func(ctx context.Context, vmId hashiVMSDK.VirtualMachineId) (string, error) { + getVMInternalID: func(ctx context.Context, vmId virtualmachines.VirtualMachineId) (string, error) { return expectedVirtualMachineID, nil }, - generalizeVM: func(context.Context, hashiVMSDK.VirtualMachineId) error { + generalizeVM: func(context.Context, virtualmachines.VirtualMachineId) error { return nil }, say: func(message string) {}, @@ -188,7 +188,7 @@ func TestStepCaptureImageShouldTakeStepArgumentsFromStateBag(t *testing.T) { var expectedComputeName = stateBag.Get(constants.ArmComputeName).(string) var expectedResourceGroupName = stateBag.Get(constants.ArmResourceGroupName).(string) - var expectedVirtualMachineCaptureParameters = stateBag.Get(constants.ArmNewVirtualMachineCaptureParameters).(*hashiVMSDK.VirtualMachineCaptureParameters) + var expectedVirtualMachineCaptureParameters = stateBag.Get(constants.ArmNewVirtualMachineCaptureParameters).(*virtualmachines.VirtualMachineCaptureParameters) actualVirtualMachineID := stateBag.Get(constants.ArmBuildVMInternalId).(string) if actualVirtualMachineID != expectedVirtualMachineID { @@ -215,12 +215,12 @@ func createTestStateBagStepCaptureImage() multistep.StateBag { stateBag.Put(constants.ArmComputeName, "Unit Test: ComputeName") stateBag.Put(constants.ArmResourceGroupName, "Unit Test: ResourceGroupName") stateBag.Put(constants.ArmSubscription, "Unit Test: SubscriptionId") - stateBag.Put(constants.ArmNewVirtualMachineCaptureParameters, &hashiVMSDK.VirtualMachineCaptureParameters{}) + stateBag.Put(constants.ArmNewVirtualMachineCaptureParameters, &virtualmachines.VirtualMachineCaptureParameters{}) stateBag.Put(constants.ArmIsManagedImage, false) stateBag.Put(constants.ArmManagedImageResourceGroupName, "") stateBag.Put(constants.ArmManagedImageName, "") - stateBag.Put(constants.ArmImageParameters, &hashiImagesSDK.Image{}) + stateBag.Put(constants.ArmImageParameters, &images.Image{}) stateBag.Put(constants.ArmIsSIGImage, false) stateBag.Put(constants.ArmSharedImageGalleryDestinationSpecialized, false) diff --git a/builder/azure/arm/step_certificate_in_keyvault.go b/builder/azure/arm/step_certificate_in_keyvault.go index ffa7a0a5..0ced001f 100644 --- a/builder/azure/arm/step_certificate_in_keyvault.go +++ b/builder/azure/arm/step_certificate_in_keyvault.go @@ -7,7 +7,7 @@ import ( "context" "fmt" - hashiSecretsSDK "github.com/hashicorp/go-azure-sdk/resource-manager/keyvault/2023-02-01/secrets" + "github.com/hashicorp/go-azure-sdk/resource-manager/keyvault/2023-02-01/secrets" "github.com/hashicorp/packer-plugin-azure/builder/azure/common/constants" "github.com/hashicorp/packer-plugin-sdk/multistep" packersdk "github.com/hashicorp/packer-plugin-sdk/packer" @@ -16,7 +16,7 @@ import ( type StepCertificateInKeyVault struct { config *Config client *AzureClient - set func(ctx context.Context, id hashiSecretsSDK.SecretId) error + set func(ctx context.Context, id secrets.SecretId) error say func(message string) error func(e error) certificate string @@ -35,9 +35,9 @@ func NewStepCertificateInKeyVault(client *AzureClient, ui packersdk.Ui, config * return step } -func (s *StepCertificateInKeyVault) setCertificate(ctx context.Context, id hashiSecretsSDK.SecretId) error { - _, err := s.client.SecretsClient.CreateOrUpdate(ctx, id, hashiSecretsSDK.SecretCreateOrUpdateParameters{ - Properties: hashiSecretsSDK.SecretProperties{ +func (s *StepCertificateInKeyVault) setCertificate(ctx context.Context, id secrets.SecretId) error { + _, err := s.client.SecretsClient.CreateOrUpdate(ctx, id, secrets.SecretCreateOrUpdateParameters{ + Properties: secrets.SecretProperties{ Value: &s.certificate, }, }) @@ -49,7 +49,7 @@ func (s *StepCertificateInKeyVault) Run(ctx context.Context, state multistep.Sta var keyVaultName = state.Get(constants.ArmKeyVaultName).(string) var subscriptionId = state.Get(constants.ArmSubscription).(string) var resourceGroupName = state.Get(constants.ArmResourceGroupName).(string) - id := hashiSecretsSDK.NewSecretID(subscriptionId, resourceGroupName, keyVaultName, DefaultSecretName) + id := secrets.NewSecretID(subscriptionId, resourceGroupName, keyVaultName, DefaultSecretName) err := s.set(ctx, id) if err != nil { s.error(fmt.Errorf("Error setting winrm cert in custom keyvault: %s", err)) diff --git a/builder/azure/arm/step_certificate_in_keyvault_test.go b/builder/azure/arm/step_certificate_in_keyvault_test.go index 618c4188..4f2f40c2 100644 --- a/builder/azure/arm/step_certificate_in_keyvault_test.go +++ b/builder/azure/arm/step_certificate_in_keyvault_test.go @@ -8,8 +8,7 @@ import ( "fmt" "testing" - hashiSecretsSDK "github.com/hashicorp/go-azure-sdk/resource-manager/keyvault/2023-02-01/secrets" - azcommon "github.com/hashicorp/packer-plugin-azure/builder/azure/common" + "github.com/hashicorp/go-azure-sdk/resource-manager/keyvault/2023-02-01/secrets" "github.com/hashicorp/packer-plugin-azure/builder/azure/common/constants" "github.com/hashicorp/packer-plugin-sdk/multistep" ) @@ -28,7 +27,7 @@ func TestNewStepCertificateInKeyVault(t *testing.T) { certKVStep := &StepCertificateInKeyVault{ say: func(message string) {}, error: func(e error) {}, - set: func(ctx context.Context, id hashiSecretsSDK.SecretId) error { return nil }, + set: func(ctx context.Context, id secrets.SecretId) error { return nil }, config: config, certificate: config.winrmCertificate} @@ -41,10 +40,6 @@ func TestNewStepCertificateInKeyVault(t *testing.T) { } func TestNewStepCertificateInKeyVault_error(t *testing.T) { - // Tell mock to return an error - cli := azcommon.MockAZVaultClient{} - cli.IsError = true - state := new(multistep.BasicStateBag) state.Put(constants.ArmKeyVaultName, "testKeyVaultName") state.Put(constants.ArmSubscription, "testSubscription") @@ -57,7 +52,7 @@ func TestNewStepCertificateInKeyVault_error(t *testing.T) { certKVStep := &StepCertificateInKeyVault{ say: func(message string) {}, error: func(e error) {}, - set: func(ctx context.Context, id hashiSecretsSDK.SecretId) error { return fmt.Errorf("Unit test fail") }, + set: func(ctx context.Context, id secrets.SecretId) error { return fmt.Errorf("Unit test fail") }, config: config, certificate: config.winrmCertificate} diff --git a/builder/azure/arm/step_create_resource_group.go b/builder/azure/arm/step_create_resource_group.go index fbb1c87d..d34cc943 100644 --- a/builder/azure/arm/step_create_resource_group.go +++ b/builder/azure/arm/step_create_resource_group.go @@ -10,7 +10,7 @@ import ( "time" "github.com/hashicorp/go-azure-helpers/resourcemanager/commonids" - hashiGroupsSDK "github.com/hashicorp/go-azure-sdk/resource-manager/resources/2022-09-01/resourcegroups" + "github.com/hashicorp/go-azure-sdk/resource-manager/resources/2022-09-01/resourcegroups" "github.com/hashicorp/packer-plugin-azure/builder/azure/common/constants" "github.com/hashicorp/packer-plugin-sdk/multistep" packersdk "github.com/hashicorp/packer-plugin-sdk/packer" @@ -38,7 +38,7 @@ func NewStepCreateResourceGroup(client *AzureClient, ui packersdk.Ui) *StepCreat func (s *StepCreateResourceGroup) createResourceGroup(ctx context.Context, subscriptionId string, resourceGroupName string, location string, tags map[string]string) error { id := commonids.NewResourceGroupID(subscriptionId, resourceGroupName) - _, err := s.client.ResourceGroupsClient.CreateOrUpdate(ctx, id, hashiGroupsSDK.ResourceGroup{ + _, err := s.client.ResourceGroupsClient.CreateOrUpdate(ctx, id, resourcegroups.ResourceGroup{ Location: location, Tags: &tags, }) @@ -142,7 +142,7 @@ func (s *StepCreateResourceGroup) Cleanup(state multistep.StateBag) { ui.Say("\nCleanup requested, deleting resource group ...") id := commonids.NewResourceGroupID(subscriptionId, resourceGroupName) if state.Get(constants.ArmAsyncResourceGroupDelete).(bool) { - _, deleteErr := s.client.ResourceGroupsClient.Delete(ctx, id, hashiGroupsSDK.DefaultDeleteOperationOptions()) + _, deleteErr := s.client.ResourceGroupsClient.Delete(ctx, id, resourcegroups.DefaultDeleteOperationOptions()) if deleteErr != nil { ui.Error(fmt.Sprintf("Error deleting resource group. Please delete it manually.\n\n"+ "Name: %s\n"+ @@ -151,7 +151,7 @@ func (s *StepCreateResourceGroup) Cleanup(state multistep.StateBag) { } s.say(fmt.Sprintf("\n Not waiting for Resource Group delete as requested by user. Resource Group Name is %s", resourceGroupName)) } else { - err := s.client.ResourceGroupsClient.DeleteThenPoll(ctx, id, hashiGroupsSDK.DefaultDeleteOperationOptions()) + err := s.client.ResourceGroupsClient.DeleteThenPoll(ctx, id, resourcegroups.DefaultDeleteOperationOptions()) if err != nil { ui.Error(fmt.Sprintf("Error deleting resource group. Please delete it manually.\n\n"+ "Name: %s\n"+ diff --git a/builder/azure/arm/step_deploy_template.go b/builder/azure/arm/step_deploy_template.go index 33efe269..325b179a 100644 --- a/builder/azure/arm/step_deploy_template.go +++ b/builder/azure/arm/step_deploy_template.go @@ -13,12 +13,12 @@ import ( "time" "github.com/hashicorp/go-azure-helpers/resourcemanager/commonids" - hashiVMSDK "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-01/virtualmachines" - hashiDisksSDK "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-02/disks" - hashiNetworkSecurityGroupsSDK "github.com/hashicorp/go-azure-sdk/resource-manager/network/2022-09-01/networksecuritygroups" - hashiVirtualNetworksSDK "github.com/hashicorp/go-azure-sdk/resource-manager/network/2022-09-01/virtualnetworks" - hashiDeploymentOperationsSDK "github.com/hashicorp/go-azure-sdk/resource-manager/resources/2022-09-01/deploymentoperations" - hashiDeploymentsSDK "github.com/hashicorp/go-azure-sdk/resource-manager/resources/2022-09-01/deployments" + "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-01/virtualmachines" + "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-02/disks" + "github.com/hashicorp/go-azure-sdk/resource-manager/network/2022-09-01/networksecuritygroups" + "github.com/hashicorp/go-azure-sdk/resource-manager/network/2022-09-01/virtualnetworks" + "github.com/hashicorp/go-azure-sdk/resource-manager/resources/2022-09-01/deploymentoperations" + "github.com/hashicorp/go-azure-sdk/resource-manager/resources/2022-09-01/deployments" "github.com/hashicorp/packer-plugin-azure/builder/azure/common/constants" "github.com/hashicorp/packer-plugin-sdk/multistep" packersdk "github.com/hashicorp/packer-plugin-sdk/packer" @@ -156,7 +156,7 @@ func (s *StepDeployTemplate) deployTemplate(ctx context.Context, subscriptionId if err != nil { return err } - id := hashiDeploymentsSDK.NewResourceGroupProviderDeploymentID(subscriptionId, resourceGroupName, deploymentName) + id := deployments.NewResourceGroupProviderDeploymentID(subscriptionId, resourceGroupName, deploymentName) err = s.client.DeploymentsClient.CreateOrUpdateThenPoll(ctx, id, *deployment) if err != nil { s.say(s.client.LastError.Error()) @@ -172,7 +172,7 @@ func (s *StepDeployTemplate) deleteDeploymentObject(ctx context.Context, state m ui := state.Get("ui").(packersdk.Ui) ui.Say(fmt.Sprintf("Removing the created Deployment object: '%s'", deploymentName)) - id := hashiDeploymentsSDK.NewResourceGroupProviderDeploymentID(subscriptionId, resourceGroupName, deploymentName) + id := deployments.NewResourceGroupProviderDeploymentID(subscriptionId, resourceGroupName, deploymentName) err := s.client.DeploymentsClient.DeleteThenPoll(ctx, id) if err != nil { return err @@ -184,8 +184,8 @@ func (s *StepDeployTemplate) getImageDetails(ctx context.Context, subscriptionId //TODO is this still true? //We can't depend on constants.ArmOSDiskVhd being set var imageName, imageType string - vmID := hashiVMSDK.NewVirtualMachineID(subscriptionId, resourceGroupName, computeName) - vm, err := s.client.VirtualMachinesClient.Get(ctx, vmID, hashiVMSDK.DefaultGetOperationOptions()) + vmID := virtualmachines.NewVirtualMachineID(subscriptionId, resourceGroupName, computeName) + vm, err := s.client.VirtualMachinesClient.Get(ctx, vmID, virtualmachines.DefaultGetOperationOptions()) if err != nil { return imageName, imageType, err } @@ -215,8 +215,8 @@ func (s *StepDeployTemplate) getImageDetails(ctx context.Context, subscriptionId func deleteResource(ctx context.Context, client *AzureClient, subscriptionId string, resourceType string, resourceName string, resourceGroupName string) error { switch resourceType { case "Microsoft.Compute/virtualMachines": - vmID := hashiVMSDK.NewVirtualMachineID(subscriptionId, resourceGroupName, resourceName) - if err := client.VirtualMachinesClient.DeleteThenPoll(ctx, vmID, hashiVMSDK.DefaultDeleteOperationOptions()); err != nil { + vmID := virtualmachines.NewVirtualMachineID(subscriptionId, resourceGroupName, resourceName) + if err := client.VirtualMachinesClient.DeleteThenPoll(ctx, vmID, virtualmachines.DefaultDeleteOperationOptions()); err != nil { return err } case "Microsoft.KeyVault/vaults": @@ -228,11 +228,11 @@ func deleteResource(ctx context.Context, client *AzureClient, subscriptionId str err := client.NetworkMetaClient.NetworkInterfaces.DeleteThenPoll(ctx, interfaceID) return err case "Microsoft.Network/virtualNetworks": - vnetID := hashiVirtualNetworksSDK.NewVirtualNetworkID(subscriptionId, resourceGroupName, resourceName) + vnetID := virtualnetworks.NewVirtualNetworkID(subscriptionId, resourceGroupName, resourceName) err := client.NetworkMetaClient.VirtualNetworks.DeleteThenPoll(ctx, vnetID) return err case "Microsoft.Network/networkSecurityGroups": - secGroupId := hashiNetworkSecurityGroupsSDK.NewNetworkSecurityGroupID(subscriptionId, resourceGroupName, resourceName) + secGroupId := networksecuritygroups.NewNetworkSecurityGroupID(subscriptionId, resourceGroupName, resourceName) err := client.NetworkMetaClient.NetworkSecurityGroups.DeleteThenPoll(ctx, secGroupId) return err case "Microsoft.Network/publicIPAddresses": @@ -250,7 +250,7 @@ func (s *StepDeployTemplate) deleteImage(ctx context.Context, imageName string, if isManagedDisk { xs := strings.Split(imageName, "/") diskName := xs[len(xs)-1] - diskId := hashiDisksSDK.NewDiskID(subscriptionId, resourceGroupName, diskName) + diskId := disks.NewDiskID(subscriptionId, resourceGroupName, diskName) if err := s.client.DisksClient.DeleteThenPoll(ctx, diskId); err != nil { return err @@ -274,9 +274,9 @@ func (s *StepDeployTemplate) deleteImage(ctx context.Context, imageName string, func (s *StepDeployTemplate) deleteDeploymentResources(ctx context.Context, subscriptionId, deploymentName, resourceGroupName string) error { var maxResources int64 = 50 - options := hashiDeploymentOperationsSDK.DefaultListOperationOptions() + options := deploymentoperations.DefaultListOperationOptions() options.Top = &maxResources - id := hashiDeploymentOperationsSDK.NewResourceGroupDeploymentID(subscriptionId, resourceGroupName, deploymentName) + id := deploymentoperations.NewResourceGroupDeploymentID(subscriptionId, resourceGroupName, deploymentName) deploymentOperations, err := s.client.DeploymentOperationsClient.ListComplete(ctx, id, options) if err != nil { s.reportIfError(err, resourceGroupName) diff --git a/builder/azure/arm/step_get_additional_disks.go b/builder/azure/arm/step_get_additional_disks.go index 46cb6055..bb878aed 100644 --- a/builder/azure/arm/step_get_additional_disks.go +++ b/builder/azure/arm/step_get_additional_disks.go @@ -8,7 +8,7 @@ import ( "errors" "fmt" - hashiVMSDK "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-01/virtualmachines" + "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-01/virtualmachines" "github.com/hashicorp/packer-plugin-azure/builder/azure/common/constants" "github.com/hashicorp/packer-plugin-sdk/multistep" @@ -17,7 +17,7 @@ import ( type StepGetDataDisk struct { client *AzureClient - query func(ctx context.Context, subscriptionId string, resourceGroupName string, computeName string) (*hashiVMSDK.VirtualMachine, error) + query func(ctx context.Context, subscriptionId string, resourceGroupName string, computeName string) (*virtualmachines.VirtualMachine, error) say func(message string) error func(e error) } @@ -33,9 +33,9 @@ func NewStepGetAdditionalDisks(client *AzureClient, ui packersdk.Ui) *StepGetDat return step } -func (s *StepGetDataDisk) queryCompute(ctx context.Context, subscriptionId string, resourceGroupName string, computeName string) (*hashiVMSDK.VirtualMachine, error) { - vmID := hashiVMSDK.NewVirtualMachineID(subscriptionId, resourceGroupName, computeName) - vm, err := s.client.VirtualMachinesClient.Get(ctx, vmID, hashiVMSDK.DefaultGetOperationOptions()) +func (s *StepGetDataDisk) queryCompute(ctx context.Context, subscriptionId string, resourceGroupName string, computeName string) (*virtualmachines.VirtualMachine, error) { + vmID := virtualmachines.NewVirtualMachineID(subscriptionId, resourceGroupName, computeName) + vm, err := s.client.VirtualMachinesClient.Get(ctx, vmID, virtualmachines.DefaultGetOperationOptions()) if err != nil { s.say(s.client.LastError.Error()) } diff --git a/builder/azure/arm/step_get_additional_disks_test.go b/builder/azure/arm/step_get_additional_disks_test.go index ce3806fc..4ec70e61 100644 --- a/builder/azure/arm/step_get_additional_disks_test.go +++ b/builder/azure/arm/step_get_additional_disks_test.go @@ -8,7 +8,7 @@ import ( "fmt" "testing" - hashiVMSDK "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-01/virtualmachines" + "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-01/virtualmachines" "github.com/hashicorp/packer-plugin-azure/builder/azure/common/constants" "github.com/hashicorp/packer-plugin-sdk/multistep" @@ -16,7 +16,7 @@ import ( func TestStepGetAdditionalDiskShouldFailIfGetFails(t *testing.T) { var testSubject = &StepGetDataDisk{ - query: func(context.Context, string, string, string) (*hashiVMSDK.VirtualMachine, error) { + query: func(context.Context, string, string, string) (*virtualmachines.VirtualMachine, error) { return createVirtualMachineWithDataDisksFromUri("test.vhd"), fmt.Errorf("!! Unit Test FAIL !!") }, say: func(message string) {}, @@ -37,7 +37,7 @@ func TestStepGetAdditionalDiskShouldFailIfGetFails(t *testing.T) { func TestStepGetAdditionalDiskShouldPassIfGetPasses(t *testing.T) { var testSubject = &StepGetDataDisk{ - query: func(context.Context, string, string, string) (*hashiVMSDK.VirtualMachine, error) { + query: func(context.Context, string, string, string) (*virtualmachines.VirtualMachine, error) { return createVirtualMachineWithDataDisksFromUri("test.vhd"), nil }, say: func(message string) {}, @@ -61,7 +61,7 @@ func TestStepGetAdditionalDiskShouldTakeValidateArgumentsFromStateBag(t *testing var actualComputeName string var actualSubscriptionId string var testSubject = &StepGetDataDisk{ - query: func(ctx context.Context, subscriptionId string, resourceGroupName string, computeName string) (*hashiVMSDK.VirtualMachine, error) { + query: func(ctx context.Context, subscriptionId string, resourceGroupName string, computeName string) (*virtualmachines.VirtualMachine, error) { actualResourceGroupName = resourceGroupName actualComputeName = computeName actualSubscriptionId = subscriptionId @@ -115,18 +115,18 @@ func createTestStateBagStepGetAdditionalDisks() multistep.StateBag { return stateBag } -func createVirtualMachineWithDataDisksFromUri(vhdUri string) *hashiVMSDK.VirtualMachine { - vm := hashiVMSDK.VirtualMachine{ - Properties: &hashiVMSDK.VirtualMachineProperties{ - StorageProfile: &hashiVMSDK.StorageProfile{ - OsDisk: &hashiVMSDK.OSDisk{ - Vhd: &hashiVMSDK.VirtualHardDisk{ +func createVirtualMachineWithDataDisksFromUri(vhdUri string) *virtualmachines.VirtualMachine { + vm := virtualmachines.VirtualMachine{ + Properties: &virtualmachines.VirtualMachineProperties{ + StorageProfile: &virtualmachines.StorageProfile{ + OsDisk: &virtualmachines.OSDisk{ + Vhd: &virtualmachines.VirtualHardDisk{ Uri: &vhdUri, }, }, - DataDisks: &[]hashiVMSDK.DataDisk{ + DataDisks: &[]virtualmachines.DataDisk{ { - Vhd: &hashiVMSDK.VirtualHardDisk{ + Vhd: &virtualmachines.VirtualHardDisk{ Uri: &vhdUri, }, }, diff --git a/builder/azure/arm/step_get_certificate.go b/builder/azure/arm/step_get_certificate.go index cecc38e3..422908dc 100644 --- a/builder/azure/arm/step_get_certificate.go +++ b/builder/azure/arm/step_get_certificate.go @@ -9,7 +9,7 @@ import ( "fmt" "time" - hashiSecretsSDK "github.com/hashicorp/go-azure-sdk/resource-manager/keyvault/2023-02-01/secrets" + "github.com/hashicorp/go-azure-sdk/resource-manager/keyvault/2023-02-01/secrets" "github.com/hashicorp/packer-plugin-azure/builder/azure/common/constants" "github.com/hashicorp/packer-plugin-sdk/multistep" packersdk "github.com/hashicorp/packer-plugin-sdk/packer" @@ -36,7 +36,7 @@ func NewStepGetCertificate(client *AzureClient, ui packersdk.Ui) *StepGetCertifi } func (s *StepGetCertificate) getCertificateUrl(ctx context.Context, subscriptionId string, resourceGroupName string, keyVaultName string, secretName string) (string, error) { - id := hashiSecretsSDK.NewSecretID(subscriptionId, resourceGroupName, keyVaultName, secretName) + id := secrets.NewSecretID(subscriptionId, resourceGroupName, keyVaultName, secretName) secret, err := s.client.SecretsClient.Get(ctx, id) if err != nil { s.say(s.client.LastError.Error()) diff --git a/builder/azure/arm/step_get_ip_address.go b/builder/azure/arm/step_get_ip_address.go index 0b410d02..63cb7251 100644 --- a/builder/azure/arm/step_get_ip_address.go +++ b/builder/azure/arm/step_get_ip_address.go @@ -8,8 +8,8 @@ import ( "fmt" "github.com/hashicorp/go-azure-helpers/resourcemanager/commonids" - hashiNetworkInterfacesSDK "github.com/hashicorp/go-azure-sdk/resource-manager/network/2022-09-01/networkinterfaces" - hashiPublicIPSDK "github.com/hashicorp/go-azure-sdk/resource-manager/network/2022-09-01/publicipaddresses" + "github.com/hashicorp/go-azure-sdk/resource-manager/network/2022-09-01/networkinterfaces" + "github.com/hashicorp/go-azure-sdk/resource-manager/network/2022-09-01/publicipaddresses" "github.com/hashicorp/packer-plugin-azure/builder/azure/common/constants" "github.com/hashicorp/packer-plugin-sdk/multistep" packersdk "github.com/hashicorp/packer-plugin-sdk/packer" @@ -61,7 +61,7 @@ func NewStepGetIPAddress(client *AzureClient, ui packersdk.Ui, endpoint Endpoint func (s *StepGetIPAddress) getPrivateIP(ctx context.Context, subscriptionId string, resourceGroupName string, ipAddressName string, interfaceName string) (string, error) { intID := commonids.NewNetworkInterfaceID(subscriptionId, resourceGroupName, interfaceName) - resp, err := s.client.NetworkMetaClient.NetworkInterfaces.Get(ctx, intID, hashiNetworkInterfacesSDK.DefaultGetOperationOptions()) + resp, err := s.client.NetworkMetaClient.NetworkInterfaces.Get(ctx, intID, networkinterfaces.DefaultGetOperationOptions()) if err != nil { s.say(s.client.LastError.Error()) return "", err @@ -72,7 +72,7 @@ func (s *StepGetIPAddress) getPrivateIP(ctx context.Context, subscriptionId stri func (s *StepGetIPAddress) getPublicIP(ctx context.Context, subscriptionId string, resourceGroupName string, ipAddressName string, interfaceName string) (string, error) { ipID := commonids.NewPublicIPAddressID(subscriptionId, resourceGroupName, ipAddressName) - resp, err := s.client.NetworkMetaClient.PublicIPAddresses.Get(ctx, ipID, hashiPublicIPSDK.DefaultGetOperationOptions()) + resp, err := s.client.NetworkMetaClient.PublicIPAddresses.Get(ctx, ipID, publicipaddresses.DefaultGetOperationOptions()) if err != nil { return "", err } diff --git a/builder/azure/arm/step_get_os_disk.go b/builder/azure/arm/step_get_os_disk.go index 676413e3..868af6fa 100644 --- a/builder/azure/arm/step_get_os_disk.go +++ b/builder/azure/arm/step_get_os_disk.go @@ -8,7 +8,7 @@ import ( "errors" "fmt" - hashiVMSDK "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-01/virtualmachines" + "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-01/virtualmachines" "github.com/hashicorp/packer-plugin-azure/builder/azure/common/constants" "github.com/hashicorp/packer-plugin-sdk/multistep" @@ -17,7 +17,7 @@ import ( type StepGetOSDisk struct { client *AzureClient - query func(ctx context.Context, resourceGroupName string, computeName string, subscriptionId string) (*hashiVMSDK.VirtualMachine, error) + query func(ctx context.Context, resourceGroupName string, computeName string, subscriptionId string) (*virtualmachines.VirtualMachine, error) say func(message string) error func(e error) } @@ -33,9 +33,9 @@ func NewStepGetOSDisk(client *AzureClient, ui packersdk.Ui) *StepGetOSDisk { return step } -func (s *StepGetOSDisk) queryCompute(ctx context.Context, resourceGroupName string, computeName string, subscriptionId string) (*hashiVMSDK.VirtualMachine, error) { - vmID := hashiVMSDK.NewVirtualMachineID(subscriptionId, resourceGroupName, computeName) - vm, err := s.client.VirtualMachinesClient.Get(ctx, vmID, hashiVMSDK.DefaultGetOperationOptions()) +func (s *StepGetOSDisk) queryCompute(ctx context.Context, resourceGroupName string, computeName string, subscriptionId string) (*virtualmachines.VirtualMachine, error) { + vmID := virtualmachines.NewVirtualMachineID(subscriptionId, resourceGroupName, computeName) + vm, err := s.client.VirtualMachinesClient.Get(ctx, vmID, virtualmachines.DefaultGetOperationOptions()) if err != nil { s.say(s.client.LastError.Error()) return nil, err diff --git a/builder/azure/arm/step_get_os_disk_test.go b/builder/azure/arm/step_get_os_disk_test.go index 8df9d20f..70afb12f 100644 --- a/builder/azure/arm/step_get_os_disk_test.go +++ b/builder/azure/arm/step_get_os_disk_test.go @@ -8,7 +8,7 @@ import ( "fmt" "testing" - hashiVMSDK "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-01/virtualmachines" + "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-01/virtualmachines" "github.com/hashicorp/packer-plugin-azure/builder/azure/common/constants" "github.com/hashicorp/packer-plugin-sdk/multistep" @@ -16,7 +16,7 @@ import ( func TestStepGetOSDiskShouldFailIfGetFails(t *testing.T) { var testSubject = &StepGetOSDisk{ - query: func(context.Context, string, string, string) (*hashiVMSDK.VirtualMachine, error) { + query: func(context.Context, string, string, string) (*virtualmachines.VirtualMachine, error) { return createVirtualMachineFromUri("test.vhd"), fmt.Errorf("!! Unit Test FAIL !!") }, say: func(message string) {}, @@ -37,7 +37,7 @@ func TestStepGetOSDiskShouldFailIfGetFails(t *testing.T) { func TestStepGetOSDiskShouldPassIfGetPasses(t *testing.T) { var testSubject = &StepGetOSDisk{ - query: func(context.Context, string, string, string) (*hashiVMSDK.VirtualMachine, error) { + query: func(context.Context, string, string, string) (*virtualmachines.VirtualMachine, error) { return createVirtualMachineFromUri("test.vhd"), nil }, say: func(message string) {}, @@ -62,7 +62,7 @@ func TestStepGetOSDiskShouldTakeValidateArgumentsFromStateBag(t *testing.T) { var actualSubscriptionId string var testSubject = &StepGetOSDisk{ - query: func(ctx context.Context, resourceGroupName string, computeName string, subscriptionId string) (*hashiVMSDK.VirtualMachine, error) { + query: func(ctx context.Context, resourceGroupName string, computeName string, subscriptionId string) (*virtualmachines.VirtualMachine, error) { actualResourceGroupName = resourceGroupName actualComputeName = computeName actualSubscriptionId = subscriptionId @@ -115,12 +115,12 @@ func createTestStateBagStepGetOSDisk() multistep.StateBag { return stateBag } -func createVirtualMachineFromUri(vhdUri string) *hashiVMSDK.VirtualMachine { - vm := hashiVMSDK.VirtualMachine{ - Properties: &hashiVMSDK.VirtualMachineProperties{ - StorageProfile: &hashiVMSDK.StorageProfile{ - OsDisk: &hashiVMSDK.OSDisk{ - Vhd: &hashiVMSDK.VirtualHardDisk{ +func createVirtualMachineFromUri(vhdUri string) *virtualmachines.VirtualMachine { + vm := virtualmachines.VirtualMachine{ + Properties: &virtualmachines.VirtualMachineProperties{ + StorageProfile: &virtualmachines.StorageProfile{ + OsDisk: &virtualmachines.OSDisk{ + Vhd: &virtualmachines.VirtualHardDisk{ Uri: &vhdUri, }, }, diff --git a/builder/azure/arm/step_get_source_image_name.go b/builder/azure/arm/step_get_source_image_name.go index 03f42113..86724cc7 100644 --- a/builder/azure/arm/step_get_source_image_name.go +++ b/builder/azure/arm/step_get_source_image_name.go @@ -6,7 +6,7 @@ import ( "log" "regexp" - hashiGalleryImageVersionsSDK "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-03/galleryimageversions" + "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-03/galleryimageversions" "github.com/hashicorp/packer-plugin-sdk/multistep" packersdk "github.com/hashicorp/packer-plugin-sdk/packer" "github.com/hashicorp/packer-plugin-sdk/packerbuilderdata" @@ -16,7 +16,7 @@ type StepGetSourceImageName struct { client *AzureClient config *Config GeneratedData *packerbuilderdata.GeneratedData - getGalleryVersion func(context.Context) (*hashiGalleryImageVersionsSDK.GalleryImageVersion, error) + getGalleryVersion func(context.Context) (*galleryimageversions.GalleryImageVersion, error) say func(message string) error func(e error) } @@ -99,11 +99,11 @@ func (s *StepGetSourceImageName) Run(ctx context.Context, state multistep.StateB return multistep.ActionContinue } -func (s *StepGetSourceImageName) GetGalleryImageVersion(ctx context.Context) (*hashiGalleryImageVersionsSDK.GalleryImageVersion, error) { +func (s *StepGetSourceImageName) GetGalleryImageVersion(ctx context.Context) (*galleryimageversions.GalleryImageVersion, error) { client := s.client.GalleryImageVersionsClient - galleryVersionId := hashiGalleryImageVersionsSDK.NewImageVersionID(s.config.SharedGallery.Subscription, s.config.SharedGallery.ResourceGroup, s.config.SharedGallery.GalleryName, s.config.SharedGallery.ImageName, s.config.SharedGallery.ImageVersion) - result, err := client.Get(ctx, galleryVersionId, hashiGalleryImageVersionsSDK.DefaultGetOperationOptions()) + galleryVersionId := galleryimageversions.NewImageVersionID(s.config.SharedGallery.Subscription, s.config.SharedGallery.ResourceGroup, s.config.SharedGallery.GalleryName, s.config.SharedGallery.ImageName, s.config.SharedGallery.ImageVersion) + result, err := client.Get(ctx, galleryVersionId, galleryimageversions.DefaultGetOperationOptions()) if err != nil { return nil, err } diff --git a/builder/azure/arm/step_get_source_image_name_test.go b/builder/azure/arm/step_get_source_image_name_test.go index 4ebc5469..f0ea1a0d 100644 --- a/builder/azure/arm/step_get_source_image_name_test.go +++ b/builder/azure/arm/step_get_source_image_name_test.go @@ -8,7 +8,7 @@ import ( "context" "testing" - hashiGalleryImageVersionsSDK "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-03/galleryimageversions" + galleryimageversions "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-03/galleryimageversions" "github.com/hashicorp/packer-plugin-azure/builder/azure/common/client" "github.com/hashicorp/packer-plugin-sdk/multistep" packersdk "github.com/hashicorp/packer-plugin-sdk/packer" @@ -25,22 +25,22 @@ func TestStepGetSourceImageName(t *testing.T) { vmSourcedSigID := "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/pkr-Resource-Group-blah/providers/Microsoft.Compute/virtualMachines/pkrvmexample" managedImageSourcedSigID := "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/pkr-Resource-Group-blah/providers/Microsoft.Compute/images/exampleimage" sigArtifactID := "example-sig-id" - vmSourcedSigImageVersion := &hashiGalleryImageVersionsSDK.GalleryImageVersion{ + vmSourcedSigImageVersion := &galleryimageversions.GalleryImageVersion{ Id: &sigArtifactID, - Properties: &hashiGalleryImageVersionsSDK.GalleryImageVersionProperties{ - StorageProfile: hashiGalleryImageVersionsSDK.GalleryImageVersionStorageProfile{ - Source: &hashiGalleryImageVersionsSDK.GalleryArtifactVersionFullSource{ + Properties: &galleryimageversions.GalleryImageVersionProperties{ + StorageProfile: galleryimageversions.GalleryImageVersionStorageProfile{ + Source: &galleryimageversions.GalleryArtifactVersionFullSource{ Id: &vmSourcedSigID, }, }, }, } - managedImageSourcedSigImageVersion := &hashiGalleryImageVersionsSDK.GalleryImageVersion{ + managedImageSourcedSigImageVersion := &galleryimageversions.GalleryImageVersion{ Id: &sigArtifactID, - Properties: &hashiGalleryImageVersionsSDK.GalleryImageVersionProperties{ - StorageProfile: hashiGalleryImageVersionsSDK.GalleryImageVersionStorageProfile{ - Source: &hashiGalleryImageVersionsSDK.GalleryArtifactVersionFullSource{ + Properties: &galleryimageversions.GalleryImageVersionProperties{ + StorageProfile: galleryimageversions.GalleryImageVersionStorageProfile{ + Source: &galleryimageversions.GalleryArtifactVersionFullSource{ Id: &managedImageSourcedSigID, }, }, @@ -51,7 +51,7 @@ func TestStepGetSourceImageName(t *testing.T) { name string config *Config expected string - mockedGalleryImage *hashiGalleryImageVersionsSDK.GalleryImageVersion + mockedGalleryImage *galleryimageversions.GalleryImageVersion }{ { name: "ImageUrl", @@ -119,7 +119,7 @@ func TestStepGetSourceImageName(t *testing.T) { GeneratedData: &genData, say: ui.Say, error: func(e error) {}, - getGalleryVersion: func(ctx context.Context) (*hashiGalleryImageVersionsSDK.GalleryImageVersion, error) { + getGalleryVersion: func(ctx context.Context) (*galleryimageversions.GalleryImageVersion, error) { return tt.mockedGalleryImage, nil }, } diff --git a/builder/azure/arm/step_power_off_compute.go b/builder/azure/arm/step_power_off_compute.go index 646ba5dd..07e9b3fb 100644 --- a/builder/azure/arm/step_power_off_compute.go +++ b/builder/azure/arm/step_power_off_compute.go @@ -7,7 +7,7 @@ import ( "context" "fmt" - hashiVMSDK "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-01/virtualmachines" + "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-01/virtualmachines" "github.com/hashicorp/packer-plugin-azure/builder/azure/common/constants" "github.com/hashicorp/packer-plugin-sdk/multistep" packersdk "github.com/hashicorp/packer-plugin-sdk/packer" @@ -33,8 +33,8 @@ func NewStepPowerOffCompute(client *AzureClient, ui packersdk.Ui) *StepPowerOffC func (s *StepPowerOffCompute) powerOffCompute(ctx context.Context, subscriptionId string, resourceGroupName string, computeName string) error { hibernate := false - vmId := hashiVMSDK.NewVirtualMachineID(subscriptionId, resourceGroupName, computeName) - err := s.client.VirtualMachinesClient.DeallocateThenPoll(ctx, vmId, hashiVMSDK.DeallocateOperationOptions{Hibernate: &hibernate}) + vmId := virtualmachines.NewVirtualMachineID(subscriptionId, resourceGroupName, computeName) + err := s.client.VirtualMachinesClient.DeallocateThenPoll(ctx, vmId, virtualmachines.DeallocateOperationOptions{Hibernate: &hibernate}) if err != nil { s.say(s.client.LastError.Error()) } diff --git a/builder/azure/arm/step_publish_to_shared_image_gallery.go b/builder/azure/arm/step_publish_to_shared_image_gallery.go index b452bb77..35df0469 100644 --- a/builder/azure/arm/step_publish_to_shared_image_gallery.go +++ b/builder/azure/arm/step_publish_to_shared_image_gallery.go @@ -7,8 +7,8 @@ import ( "context" "fmt" - hashiImagesSDK "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-01/images" - hashiGalleryImageVersionsSDK "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-03/galleryimageversions" + "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-01/images" + "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-03/galleryimageversions" "github.com/hashicorp/packer-plugin-azure/builder/azure/common/constants" "github.com/hashicorp/packer-plugin-sdk/multistep" packersdk "github.com/hashicorp/packer-plugin-sdk/packer" @@ -40,13 +40,13 @@ func NewStepPublishToSharedImageGallery(client *AzureClient, ui packersdk.Ui, co return step } -func getSigDestinationStorageAccountType(s string) (hashiGalleryImageVersionsSDK.StorageAccountType, error) { +func getSigDestinationStorageAccountType(s string) (galleryimageversions.StorageAccountType, error) { if s == "" { - return hashiGalleryImageVersionsSDK.StorageAccountTypeStandardLRS, nil + return galleryimageversions.StorageAccountTypeStandardLRS, nil } - for _, t := range hashiGalleryImageVersionsSDK.PossibleValuesForStorageAccountType() { + for _, t := range galleryimageversions.PossibleValuesForStorageAccountType() { if s == t { - return hashiGalleryImageVersionsSDK.StorageAccountType(t), nil + return galleryimageversions.StorageAccountType(t), nil } } return "", fmt.Errorf("not an accepted value for shared_image_gallery_destination.storage_account_type") @@ -73,10 +73,10 @@ func getSigDestination(state multistep.StateBag) SharedImageGalleryDestination { } func (s *StepPublishToSharedImageGallery) publishToSig(ctx context.Context, subscriptionID string, sourceID string, sharedImageGallery SharedImageGalleryDestination, miSGImageVersionEndOfLifeDate string, miSGImageVersionExcludeFromLatest bool, miSigReplicaCount int64, location string, diskEncryptionSetId string, tags map[string]string) (string, error) { - replicationRegions := make([]hashiGalleryImageVersionsSDK.TargetRegion, len(sharedImageGallery.SigDestinationReplicationRegions)) + replicationRegions := make([]galleryimageversions.TargetRegion, len(sharedImageGallery.SigDestinationReplicationRegions)) for i, v := range sharedImageGallery.SigDestinationReplicationRegions { regionName := v - replicationRegions[i] = hashiGalleryImageVersionsSDK.TargetRegion{Name: regionName} + replicationRegions[i] = galleryimageversions.TargetRegion{Name: regionName} } storageAccountType, err := getSigDestinationStorageAccountType(sharedImageGallery.SigDestinationStorageAccountType) @@ -87,24 +87,24 @@ func (s *StepPublishToSharedImageGallery) publishToSig(ctx context.Context, subs if diskEncryptionSetId != "" { for index, targetRegion := range replicationRegions { - targetRegion.Encryption = &hashiGalleryImageVersionsSDK.EncryptionImages{ - OsDiskImage: &hashiGalleryImageVersionsSDK.OSDiskImageEncryption{ + targetRegion.Encryption = &galleryimageversions.EncryptionImages{ + OsDiskImage: &galleryimageversions.OSDiskImageEncryption{ DiskEncryptionSetId: &diskEncryptionSetId, }, } replicationRegions[index] = targetRegion } } - galleryImageVersion := hashiGalleryImageVersionsSDK.GalleryImageVersion{ + galleryImageVersion := galleryimageversions.GalleryImageVersion{ Location: location, Tags: &tags, - Properties: &hashiGalleryImageVersionsSDK.GalleryImageVersionProperties{ - StorageProfile: hashiGalleryImageVersionsSDK.GalleryImageVersionStorageProfile{ - Source: &hashiGalleryImageVersionsSDK.GalleryArtifactVersionFullSource{ + Properties: &galleryimageversions.GalleryImageVersionProperties{ + StorageProfile: galleryimageversions.GalleryImageVersionStorageProfile{ + Source: &galleryimageversions.GalleryArtifactVersionFullSource{ Id: &sourceID, }, }, - PublishingProfile: &hashiGalleryImageVersionsSDK.GalleryArtifactPublishingProfileBase{ + PublishingProfile: &galleryimageversions.GalleryArtifactPublishingProfileBase{ TargetRegions: &replicationRegions, EndOfLifeDate: &miSGImageVersionEndOfLifeDate, ExcludeFromLatest: &miSGImageVersionExcludeFromLatest, @@ -114,14 +114,14 @@ func (s *StepPublishToSharedImageGallery) publishToSig(ctx context.Context, subs }, } - galleryImageVersionId := hashiGalleryImageVersionsSDK.NewImageVersionID(subscriptionID, sharedImageGallery.SigDestinationResourceGroup, sharedImageGallery.SigDestinationGalleryName, sharedImageGallery.SigDestinationImageName, sharedImageGallery.SigDestinationImageVersion) + galleryImageVersionId := galleryimageversions.NewImageVersionID(subscriptionID, sharedImageGallery.SigDestinationResourceGroup, sharedImageGallery.SigDestinationGalleryName, sharedImageGallery.SigDestinationImageName, sharedImageGallery.SigDestinationImageVersion) err = s.client.GalleryImageVersionsClient.CreateOrUpdateThenPoll(ctx, galleryImageVersionId, galleryImageVersion) if err != nil { s.say(s.client.LastError.Error()) return "", err } - createdSGImageVersion, err := s.client.GalleryImageVersionsClient.Get(ctx, galleryImageVersionId, hashiGalleryImageVersionsSDK.DefaultGetOperationOptions()) + createdSGImageVersion, err := s.client.GalleryImageVersionsClient.Get(ctx, galleryImageVersionId, galleryimageversions.DefaultGetOperationOptions()) if err != nil { s.say(s.client.LastError.Error()) @@ -153,7 +153,7 @@ func (s *StepPublishToSharedImageGallery) Run(ctx context.Context, stateBag mult managedImageSubscription := stateBag.Get(constants.ArmManagedImageSubscription).(string) sourceID = fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Compute/images/%s", managedImageSubscription, targetManagedImageResourceGroupName, targetManagedImageName) } else { - var imageParameters = stateBag.Get(constants.ArmImageParameters).(*hashiImagesSDK.Image) + var imageParameters = stateBag.Get(constants.ArmImageParameters).(*images.Image) sourceID = *imageParameters.Properties.SourceVirtualMachine.Id } 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 index 92375b30..3ae8732b 100644 --- a/builder/azure/arm/step_publish_to_shared_image_gallery_test.go +++ b/builder/azure/arm/step_publish_to_shared_image_gallery_test.go @@ -7,7 +7,7 @@ import ( "context" "testing" - hashiImagesSDK "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-01/images" + "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-01/images" "github.com/hashicorp/packer-plugin-azure/builder/azure/common" "github.com/hashicorp/packer-plugin-azure/builder/azure/common/constants" @@ -96,8 +96,8 @@ func createTestStateBagStepPublishToSharedImageGallery(managed bool) multistep.S stateBag.Put(constants.ArmManagedImageResourceGroupName, "Unit Test: ManagedImageResourceGroupName") stateBag.Put(constants.ArmManagedImageName, "Unit Test: ManagedImageName") } else { - stateBag.Put(constants.ArmImageParameters, &hashiImagesSDK.Image{Properties: &hashiImagesSDK.ImageProperties{ - SourceVirtualMachine: &hashiImagesSDK.SubResource{Id: common.StringPtr("Unit Test: VM ID")}, + stateBag.Put(constants.ArmImageParameters, &images.Image{Properties: &images.ImageProperties{ + SourceVirtualMachine: &images.SubResource{Id: common.StringPtr("Unit Test: VM ID")}, }}) } stateBag.Put(constants.ArmManagedImageSubscription, "Unit Test: ManagedImageSubscription") diff --git a/builder/azure/arm/step_snapshot_data_disks.go b/builder/azure/arm/step_snapshot_data_disks.go index bcb8b553..4f1afb08 100644 --- a/builder/azure/arm/step_snapshot_data_disks.go +++ b/builder/azure/arm/step_snapshot_data_disks.go @@ -8,7 +8,7 @@ import ( "fmt" "strconv" - hashiSnapshotsSDK "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-02/snapshots" + "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-02/snapshots" "github.com/hashicorp/packer-plugin-azure/builder/azure/common" "github.com/hashicorp/packer-plugin-azure/builder/azure/common/constants" "github.com/hashicorp/packer-plugin-sdk/multistep" @@ -36,17 +36,17 @@ func NewStepSnapshotDataDisks(client *AzureClient, ui packersdk.Ui, config *Conf } func (s *StepSnapshotDataDisks) createDataDiskSnapshot(ctx context.Context, subscriptionId string, resourceGroupName string, srcUriVhd string, location string, tags map[string]string, dstSnapshotName string) error { - srcVhdToSnapshot := hashiSnapshotsSDK.Snapshot{ - Properties: &hashiSnapshotsSDK.SnapshotProperties{ - CreationData: hashiSnapshotsSDK.CreationData{ - CreateOption: hashiSnapshotsSDK.DiskCreateOptionCopy, + srcVhdToSnapshot := snapshots.Snapshot{ + Properties: &snapshots.SnapshotProperties{ + CreationData: snapshots.CreationData{ + CreateOption: snapshots.DiskCreateOptionCopy, SourceResourceId: common.StringPtr(srcUriVhd), }, }, Location: *common.StringPtr(location), Tags: &tags, } - id := hashiSnapshotsSDK.NewSnapshotID(subscriptionId, resourceGroupName, dstSnapshotName) + id := snapshots.NewSnapshotID(subscriptionId, resourceGroupName, dstSnapshotName) err := s.client.SnapshotsClient.CreateOrUpdateThenPoll(ctx, id, srcVhdToSnapshot) if err != nil { diff --git a/builder/azure/arm/step_snapshot_os_disk.go b/builder/azure/arm/step_snapshot_os_disk.go index 7e04eebb..8d623a00 100644 --- a/builder/azure/arm/step_snapshot_os_disk.go +++ b/builder/azure/arm/step_snapshot_os_disk.go @@ -7,7 +7,7 @@ import ( "context" "fmt" - hashiSnapshotsSDK "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-02/snapshots" + "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-02/snapshots" "github.com/hashicorp/packer-plugin-azure/builder/azure/common" "github.com/hashicorp/packer-plugin-azure/builder/azure/common/constants" "github.com/hashicorp/packer-plugin-sdk/multistep" @@ -36,17 +36,17 @@ func NewStepSnapshotOSDisk(client *AzureClient, ui packersdk.Ui, config *Config) func (s *StepSnapshotOSDisk) createSnapshot(ctx context.Context, subscriptionId string, resourceGroupName string, srcUriVhd string, location string, tags map[string]string, dstSnapshotName string) error { - srcVhdToSnapshot := hashiSnapshotsSDK.Snapshot{ - Properties: &hashiSnapshotsSDK.SnapshotProperties{ - CreationData: hashiSnapshotsSDK.CreationData{ - CreateOption: hashiSnapshotsSDK.DiskCreateOptionCopy, + srcVhdToSnapshot := snapshots.Snapshot{ + Properties: &snapshots.SnapshotProperties{ + CreationData: snapshots.CreationData{ + CreateOption: snapshots.DiskCreateOptionCopy, SourceResourceId: common.StringPtr(srcUriVhd), }, }, Location: *common.StringPtr(location), Tags: &tags, } - id := hashiSnapshotsSDK.NewSnapshotID(subscriptionId, resourceGroupName, dstSnapshotName) + id := snapshots.NewSnapshotID(subscriptionId, resourceGroupName, dstSnapshotName) err := s.client.SnapshotsClient.CreateOrUpdateThenPoll(ctx, id, srcVhdToSnapshot) if err != nil { diff --git a/builder/azure/arm/step_validate_template.go b/builder/azure/arm/step_validate_template.go index 6b69eceb..603fe6f8 100644 --- a/builder/azure/arm/step_validate_template.go +++ b/builder/azure/arm/step_validate_template.go @@ -7,7 +7,7 @@ import ( "context" "fmt" - hashiDeploymentsSDK "github.com/hashicorp/go-azure-sdk/resource-manager/resources/2022-09-01/deployments" + "github.com/hashicorp/go-azure-sdk/resource-manager/resources/2022-09-01/deployments" "github.com/hashicorp/packer-plugin-azure/builder/azure/common/constants" "github.com/hashicorp/packer-plugin-sdk/multistep" packersdk "github.com/hashicorp/packer-plugin-sdk/packer" @@ -42,7 +42,7 @@ func (s *StepValidateTemplate) validateTemplate(ctx context.Context, subscriptio if err != nil { return err } - id := hashiDeploymentsSDK.NewResourceGroupProviderDeploymentID(subscriptionId, resourceGroupName, deploymentName) + id := deployments.NewResourceGroupProviderDeploymentID(subscriptionId, resourceGroupName, deploymentName) _, err = s.client.DeploymentsClient.Validate(ctx, id, *deployment) if err != nil { s.say(s.client.LastError.Error()) diff --git a/builder/azure/chroot/builder.go b/builder/azure/chroot/builder.go index 80df03ab..09b935be 100644 --- a/builder/azure/chroot/builder.go +++ b/builder/azure/chroot/builder.go @@ -18,6 +18,8 @@ import ( "runtime" "strings" + "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-01/images" + "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-01/virtualmachines" "github.com/hashicorp/hcl/v2/hcldec" azcommon "github.com/hashicorp/packer-plugin-azure/builder/azure/common" "github.com/hashicorp/packer-plugin-azure/builder/azure/common/client" @@ -30,8 +32,6 @@ import ( "github.com/hashicorp/packer-plugin-sdk/template/config" "github.com/hashicorp/packer-plugin-sdk/template/interpolate" - "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" ) @@ -88,7 +88,7 @@ type Config struct { // 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"` + OSDiskSizeGB int64 `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"` @@ -262,23 +262,23 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) { } if b.config.OSDiskStorageAccountType == "" { - b.config.OSDiskStorageAccountType = string(compute.PremiumLRS) + b.config.OSDiskStorageAccountType = string(virtualmachines.StorageAccountTypesPremiumLRS) } if b.config.OSDiskCacheType == "" { - b.config.OSDiskCacheType = string(compute.CachingTypesReadOnly) + b.config.OSDiskCacheType = string(virtualmachines.CachingTypesReadOnly) } if b.config.DataDiskStorageAccountType == "" { - b.config.DataDiskStorageAccountType = string(compute.PremiumLRS) + b.config.DataDiskStorageAccountType = string(virtualmachines.StorageAccountTypesPremiumLRS) } if b.config.DataDiskCacheType == "" { - b.config.DataDiskCacheType = string(compute.CachingTypesReadOnly) + b.config.DataDiskCacheType = string(virtualmachines.CachingTypesReadOnly) } if b.config.ImageHyperVGeneration == "" { - b.config.ImageHyperVGeneration = string(compute.V1) + b.config.ImageHyperVGeneration = string(virtualmachines.HyperVGenerationTypeVOne) } // checks, accumulate any errors or warnings @@ -333,10 +333,10 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) { } if b.config.ImageResourceID != "" { - r, err := azure.ParseResourceID(b.config.ImageResourceID) + r, err := client.ParseResourceID(b.config.ImageResourceID) if err != nil || !strings.EqualFold(r.Provider, "Microsoft.Compute") || - !strings.EqualFold(r.ResourceType, "images") { + !strings.EqualFold(r.ResourceType.String(), "images") { errs = packersdk.MultiErrorAppend(fmt.Errorf( "image_resource_id: %q is not a valid image resource id", b.config.ImageResourceID)) } @@ -371,33 +371,33 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) { } func checkDiskCacheType(s string) interface{} { - for _, v := range compute.PossibleCachingTypesValues() { - if compute.CachingTypes(s) == v { + for _, v := range virtualmachines.PossibleValuesForCachingTypes() { + if string(virtualmachines.CachingTypes(s)) == v { return nil } } return fmt.Errorf("%q is not a valid value %v", - s, compute.PossibleCachingTypesValues()) + s, virtualmachines.PossibleValuesForCachingTypes()) } func checkStorageAccountType(s string) interface{} { - for _, v := range compute.PossibleDiskStorageAccountTypesValues() { - if compute.DiskStorageAccountTypes(s) == v { + for _, v := range virtualmachines.PossibleValuesForStorageAccountTypes() { + if string(virtualmachines.StorageAccountTypes(s)) == v { return nil } } return fmt.Errorf("%q is not a valid value %v", - s, compute.PossibleDiskStorageAccountTypesValues()) + s, virtualmachines.PossibleValuesForStorageAccountTypes()) } func checkHyperVGeneration(s string) interface{} { - for _, v := range compute.PossibleHyperVGenerationValues() { - if compute.HyperVGeneration(s) == v { + for _, v := range virtualmachines.PossibleValuesForHyperVGenerationType() { + if string(virtualmachines.HyperVGenerationType(s)) == v { return nil } } return fmt.Errorf("%q is not a valid value %v", - s, compute.PossibleHyperVGenerationValues()) + s, virtualmachines.PossibleValuesForHyperVGenerationType()) } func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) (packersdk.Artifact, error) { @@ -503,38 +503,41 @@ func buildsteps( if hasValidSharedImage { // validate destination early addSteps( - &StepVerifySharedImageDestination{ - Image: config.SharedImageGalleryDestination, - Location: info.Location, - }, + NewStepVerifySharedImageDestination( + &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}) + addSteps(NewStepCreateNewDiskset( + &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{ + NewStepResolvePlatformImageVersion(&StepResolvePlatformImageVersion{ PlatformImage: pi, Location: info.Location, - }) + }), + ) } addSteps( - &StepGetSourceImageName{ + NewStepGetSourceImageName(&StepGetSourceImageName{ GeneratedData: generatedData, SourcePlatformImage: pi, Location: info.Location, - }, - &StepCreateNewDiskset{ + }), + NewStepCreateNewDiskset(&StepCreateNewDiskset{ OSDiskID: config.TemporaryOSDiskID, OSDiskSizeGB: config.OSDiskSizeGB, OSDiskStorageAccountType: config.OSDiskStorageAccountType, @@ -543,7 +546,7 @@ func buildsteps( SourcePlatformImage: pi, SkipCleanup: config.SkipCleanup, - }, + }), ) } else { panic("Couldn't parse platfrom image urn: " + config.Source + " err: " + err.Error()) @@ -551,16 +554,16 @@ func buildsteps( case sourceDisk: addSteps( - &StepVerifySourceDisk{ + NewStepVerifySourceDisk(&StepVerifySourceDisk{ SourceDiskResourceID: config.Source, Location: info.Location, - }, - &StepGetSourceImageName{ + }), + NewStepGetSourceImageName(&StepGetSourceImageName{ GeneratedData: generatedData, SourceOSDiskResourceID: config.Source, Location: info.Location, - }, - &StepCreateNewDiskset{ + }), + NewStepCreateNewDiskset(&StepCreateNewDiskset{ OSDiskID: config.TemporaryOSDiskID, OSDiskSizeGB: config.OSDiskSizeGB, OSDiskStorageAccountType: config.OSDiskStorageAccountType, @@ -569,22 +572,22 @@ func buildsteps( Location: info.Location, SkipCleanup: config.SkipCleanup, - }, + }), ) case sourceSharedImage: addSteps( - &StepVerifySharedImageSource{ + NewStepVerifySharedImageSource(&StepVerifySharedImageSource{ SharedImageID: config.Source, SubscriptionID: info.SubscriptionID, Location: info.Location, - }, - &StepGetSourceImageName{ + }), + NewStepGetSourceImageName(&StepGetSourceImageName{ GeneratedData: generatedData, SourceImageResourceID: config.Source, Location: info.Location, - }, - &StepCreateNewDiskset{ + }), + NewStepCreateNewDiskset(&StepCreateNewDiskset{ OSDiskID: config.TemporaryOSDiskID, DataDiskIDPrefix: config.TemporaryDataDiskIDPrefix, OSDiskSizeGB: config.OSDiskSizeGB, @@ -594,7 +597,7 @@ func buildsteps( Location: info.Location, SkipCleanup: config.SkipCleanup, - }, + }), ) default: @@ -630,32 +633,32 @@ func buildsteps( if config.ImageResourceID != "" { captureSteps = append( captureSteps, - &StepCreateImage{ + NewStepCreateImage(&StepCreateImage{ ImageResourceID: config.ImageResourceID, - ImageOSState: string(compute.Generalized), + ImageOSState: string(images.OperatingSystemStateTypesGeneralized), OSDiskCacheType: config.OSDiskCacheType, OSDiskStorageAccountType: config.OSDiskStorageAccountType, Location: info.Location, - }, + }), ) } if hasValidSharedImage { captureSteps = append( captureSteps, - &StepCreateSnapshotset{ + NewStepCreateSnapshotset(&StepCreateSnapshotset{ OSDiskSnapshotID: config.TemporaryOSDiskSnapshotID, DataDiskSnapshotIDPrefix: config.TemporaryDataDiskSnapshotIDPrefix, Location: info.Location, SkipCleanup: config.SkipCleanup, - }, + }), ) captureSteps = append( captureSteps, - &StepCreateSharedImageVersion{ + NewStepCreateSharedImageVersion(&StepCreateSharedImageVersion{ Destination: config.SharedImageGalleryDestination, OSDiskCacheType: config.OSDiskCacheType, Location: info.Location, - }, + }), ) } diff --git a/builder/azure/chroot/builder.hcl2spec.go b/builder/azure/chroot/builder.hcl2spec.go index 8c4da9ab..9514ef89 100644 --- a/builder/azure/chroot/builder.hcl2spec.go +++ b/builder/azure/chroot/builder.hcl2spec.go @@ -30,7 +30,6 @@ type FlatConfig struct { 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"` - UseInteractiveAuth *bool `mapstructure:"use_interactive_auth" required:"false" cty:"use_interactive_auth" hcl:"use_interactive_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"` @@ -41,7 +40,7 @@ type FlatConfig struct { 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"` + OSDiskSizeGB *int64 `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"` @@ -88,7 +87,6 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "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}, - "use_interactive_auth": &hcldec.AttrSpec{Name: "use_interactive_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}, diff --git a/builder/azure/chroot/builder_test.go b/builder/azure/chroot/builder_test.go index 33762ac1..90684717 100644 --- a/builder/azure/chroot/builder_test.go +++ b/builder/azure/chroot/builder_test.go @@ -7,11 +7,11 @@ import ( "strings" "testing" + "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-01/virtualmachines" + "github.com/hashicorp/packer-plugin-azure/builder/azure/common/client" "github.com/hashicorp/packer-plugin-sdk/multistep" "github.com/hashicorp/packer-plugin-sdk/packerbuilderdata" - - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-12-01/compute" ) func TestBuilder_Prepare(t *testing.T) { @@ -45,14 +45,14 @@ func TestBuilder_Prepare(t *testing.T) { 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.OSDiskStorageAccountType != string(virtualmachines.StorageAccountTypesPremiumLRS) { + t.Errorf("Expected OSDiskStorageAccountType to be %s, but found %s", string(virtualmachines.StorageAccountTypesPremiumLRS), 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.OSDiskCacheType != string(virtualmachines.CachingTypesReadOnly) { + t.Errorf("Expected OSDiskCacheType to be %s, but found %s", string(virtualmachines.CachingTypesReadOnly), c.OSDiskCacheType) } - if c.ImageHyperVGeneration != string(compute.V1) { - t.Errorf("Expected ImageHyperVGeneration to be %s, but found %s", string(compute.V1), c.ImageHyperVGeneration) + if c.ImageHyperVGeneration != string(virtualmachines.HyperVGenerationTypeVOne) { + t.Errorf("Expected ImageHyperVGeneration to be %s, but found %s", string(virtualmachines.HyperVGenerationTypeVOne), c.ImageHyperVGeneration) } }, }, diff --git a/builder/azure/chroot/diskattacher.go b/builder/azure/chroot/diskattacher.go index fb18e790..e143e39f 100644 --- a/builder/azure/chroot/diskattacher.go +++ b/builder/azure/chroot/diskattacher.go @@ -10,15 +10,14 @@ import ( "strings" "time" - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2021-11-01/compute" - "github.com/Azure/go-autorest/autorest/to" + hashiVMSDK "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-01/virtualmachines" "github.com/hashicorp/packer-plugin-azure/builder/azure/common/client" packersdk "github.com/hashicorp/packer-plugin-sdk/packer" ) type DiskAttacher interface { - AttachDisk(ctx context.Context, disk string) (lun int32, err error) - WaitForDevice(ctx context.Context, i int32) (device string, err error) + AttachDisk(ctx context.Context, disk string) (lun int64, err error) + WaitForDevice(ctx context.Context, i int64) (device string, err error) DetachDisk(ctx context.Context, disk string) (err error) WaitForDetach(ctx context.Context, diskID string) error } @@ -38,26 +37,27 @@ type diskAttacher struct { } var DiskNotFoundError = errors.New("Disk not found") +var AzureAPIDiskError = errors.New("Azure API returned invalid disk") 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 { log.Printf("DetachDisk.getDisks: error: %+v\n", err) - log.Println("Checking to see if instance is part of a VM scale set before giving up.") - da.ui.Say("Initial call for fetching VM instance disks returned an error. Checking to see if instance is part of a VM scale set before giving up.") - currentDisks, err = da.getScaleSetDisks(ctx) - if err != nil { - return err - } + return err } log.Printf("Removing %q from list of disks currently attached to VM", diskID) - newDisks := []compute.DataDisk{} + newDisks := []hashiVMSDK.DataDisk{} for _, disk := range currentDisks { - if disk.ManagedDisk != nil && - !strings.EqualFold(to.String(disk.ManagedDisk.ID), diskID) { - newDisks = append(newDisks, disk) + if disk.ManagedDisk != nil { + if disk.ManagedDisk.Id == nil { + log.Println("DetatchDisks failure: Azure Client returned a disk without an ID") + return AzureAPIDiskError + } + if !strings.EqualFold(*disk.ManagedDisk.Id, diskID) { + newDisks = append(newDisks, disk) + } } } if len(currentDisks) == len(newDisks) { @@ -68,12 +68,8 @@ func (da *diskAttacher) DetachDisk(ctx context.Context, diskID string) error { err = da.setDisks(ctx, newDisks) if err != nil { log.Printf("DetachDisk.setDisks: error: %+v\n", err) - log.Println("Checking to see if instance is part of a VM scale set before giving up.") - da.ui.Say("Initial call for setting VM instance disks returned an error. Checking to see if instance is part of a VM scale set before giving up.") - err = da.setScaleSetDisks(ctx, newDisks) - if err != nil { - return err - } + return err + } return nil @@ -84,11 +80,7 @@ func (da *diskAttacher) WaitForDetach(ctx context.Context, diskID string) error list, err := da.getDisks(ctx) if err != nil { log.Printf("WaitForDetach.getDisks: error: %+v\n", err) - log.Println("Checking to see if instance is part of a VM scale set before giving up.") - list, err = da.getScaleSetDisks(ctx) - if err != nil { - return err - } + return err } if findDiskInList(list, diskID) == nil { log.Println("Disk is no longer in VM model, assuming detached") @@ -103,36 +95,31 @@ func (da *diskAttacher) WaitForDetach(ctx context.Context, diskID string) error } } -func (da *diskAttacher) AttachDisk(ctx context.Context, diskID string) (int32, error) { +func (da *diskAttacher) AttachDisk(ctx context.Context, diskID string) (int64, error) { dataDisks, err := da.getDisks(ctx) if err != nil { log.Printf("AttachDisk.getDisks: error: %+v\n", err) - log.Println("Checking to see if instance is part of a VM scale set before giving up.") - da.ui.Say("Initial call for fetching VM instance disks returned an error. Checking to see if instance is part of a VM scale set before giving up.") - dataDisks, err = da.getScaleSetDisks(ctx) - if err != nil { - return -1, err - } + 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 { + if disk.Lun == 0 { 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 + return disk.Lun, nil } // disk was not found on VM, go and actually attach it // TODO This assignment looks like it would do nothing, consider removing it //nolint - var lun int32 = -1 + var lun int64 = -1 findFreeLun: for lun = 0; lun < 64; lun++ { for _, v := range dataDisks { - if to.Int32(v.Lun) == lun { + if v.Lun == lun { continue findFreeLun } } @@ -141,127 +128,76 @@ findFreeLun: } // append new data disk to collection - dataDisks = append(dataDisks, compute.DataDisk{ - CreateOption: compute.DiskCreateOptionTypesAttach, - ManagedDisk: &compute.ManagedDiskParameters{ - ID: to.StringPtr(diskID), + dataDisks = append(dataDisks, hashiVMSDK.DataDisk{ + CreateOption: hashiVMSDK.DiskCreateOptionTypesAttach, + ManagedDisk: &hashiVMSDK.ManagedDiskParameters{ + Id: &diskID, }, - Lun: to.Int32Ptr(lun), + Lun: lun, }) // prepare resource object for update operation err = da.setDisks(ctx, dataDisks) if err != nil { log.Printf("AttachDisk.setDisks: error: %+v\n", err) - log.Println("Checking to see if instance is part of a VM scale set before giving up.") - da.ui.Say("Initial call for setting VM instance disks returned an error. Checking to see if instance is part of a VM scale set before giving up.") - err = da.setScaleSetDisks(ctx, dataDisks) - if err != nil { - return -1, err - } + return -1, err } return lun, nil } -func (da *diskAttacher) getThisVM(ctx context.Context) (compute.VirtualMachine, error) { +func (da *diskAttacher) getThisVM(ctx context.Context) (hashiVMSDK.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 + return hashiVMSDK.VirtualMachine{}, err } da.vm = vm } + vmID := hashiVMSDK.NewVirtualMachineID(da.azcli.SubscriptionID(), da.vm.ResourceGroupName, da.vm.Name) // retrieve actual VM - vmResource, err := da.azcli.VirtualMachinesClient().Get(ctx, da.vm.ResourceGroupName, da.vm.Name, "") + vmResource, err := da.azcli.VirtualMachinesClient().Get(ctx, vmID, hashiVMSDK.DefaultGetOperationOptions()) if err != nil { - return compute.VirtualMachine{}, err + return hashiVMSDK.VirtualMachine{}, err } - if vmResource.StorageProfile == nil { - return compute.VirtualMachine{}, errors.New("properties.storageProfile is not set on VM, this is unexpected") + if vmResource.Model.Properties.StorageProfile == nil { + return hashiVMSDK.VirtualMachine{}, errors.New("properties.storageProfile is not set on VM, this is unexpected") } - return vmResource, nil + return *vmResource.Model, nil } -func (da *diskAttacher) getThisScaleSetVM(ctx context.Context) (compute.VirtualMachineScaleSetVM, error) { - // getting resource info for this VM - if da.vm == nil { - vm, err := da.azcli.MetadataClient().GetComputeInfo() - if err != nil { - return compute.VirtualMachineScaleSetVM{}, err - } - da.vm = vm - } - - // retrieve actual VM - scaleSetVMInstanceID := da.vm.ResourceID[strings.LastIndex(da.vm.ResourceID, "/")+1:] - vmResource, err := da.azcli.VirtualMachineScaleSetVMsClient().Get(ctx, da.vm.ResourceGroupName, da.vm.VmScaleSetName, scaleSetVMInstanceID, "") - if err != nil { - return compute.VirtualMachineScaleSetVM{}, err - } - if vmResource.StorageProfile == nil { - return compute.VirtualMachineScaleSetVM{}, 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) { +func (da diskAttacher) getDisks(ctx context.Context) ([]hashiVMSDK.DataDisk, error) { vmResource, err := da.getThisVM(ctx) if err != nil { - return []compute.DataDisk{}, err - } - - return *vmResource.StorageProfile.DataDisks, nil -} - -func (da diskAttacher) getScaleSetDisks(ctx context.Context) ([]compute.DataDisk, error) { - vmResource, err := da.getThisScaleSetVM(ctx) - if err != nil { - return []compute.DataDisk{}, err + return []hashiVMSDK.DataDisk{}, err } - return *vmResource.StorageProfile.DataDisks, nil + return *vmResource.Properties.StorageProfile.DataDisks, nil } -func (da diskAttacher) setDisks(ctx context.Context, disks []compute.DataDisk) error { +func (da diskAttacher) setDisks(ctx context.Context, disks []hashiVMSDK.DataDisk) error { vmResource, err := da.getThisVM(ctx) if err != nil { return err } - vmResource.StorageProfile.DataDisks = &disks - vmResource.Resources = nil - - // update the VM resource, attach disk - _, err = da.azcli.VirtualMachinesClient().CreateOrUpdate(ctx, da.vm.ResourceGroupName, da.vm.Name, vmResource) - - return err -} - -func (da diskAttacher) setScaleSetDisks(ctx context.Context, disks []compute.DataDisk) error { - vmResource, err := da.getThisScaleSetVM(ctx) - if err != nil { - return err - } - - vmResource.StorageProfile.DataDisks = &disks + vmResource.Properties.StorageProfile.DataDisks = &disks vmResource.Resources = nil + vmID := hashiVMSDK.NewVirtualMachineID(da.azcli.SubscriptionID(), da.vm.ResourceGroupName, da.vm.Name) // update the VM resource, attach disk - scaleSetVMInstanceID := da.vm.ResourceID[strings.LastIndex(da.vm.ResourceID, "/")+1:] - _, err = da.azcli.VirtualMachineScaleSetVMsClient().Update(ctx, da.vm.ResourceGroupName, da.vm.VmScaleSetName, scaleSetVMInstanceID, vmResource) + _, err = da.azcli.VirtualMachinesClient().CreateOrUpdate(ctx, vmID, vmResource) return err } -func findDiskInList(list []compute.DataDisk, diskID string) *compute.DataDisk { +func findDiskInList(list []hashiVMSDK.DataDisk, diskID string) *hashiVMSDK.DataDisk { for _, disk := range list { if disk.ManagedDisk != nil && - strings.EqualFold(to.String(disk.ManagedDisk.ID), diskID) { + strings.EqualFold(*(disk.ManagedDisk.Id), diskID) { return &disk } } diff --git a/builder/azure/chroot/diskattacher_freebsd.go b/builder/azure/chroot/diskattacher_freebsd.go index fa231720..e5c46a17 100644 --- a/builder/azure/chroot/diskattacher_freebsd.go +++ b/builder/azure/chroot/diskattacher_freebsd.go @@ -14,7 +14,7 @@ import ( "time" ) -func (da diskAttacher) WaitForDevice(ctx context.Context, lun int32) (device string, err error) { +func (da diskAttacher) WaitForDevice(ctx context.Context, lun int64) (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. diff --git a/builder/azure/chroot/diskattacher_linux.go b/builder/azure/chroot/diskattacher_linux.go index 033df52b..db32d718 100644 --- a/builder/azure/chroot/diskattacher_linux.go +++ b/builder/azure/chroot/diskattacher_linux.go @@ -12,11 +12,11 @@ import ( "time" ) -func diskPathForLun(lun int32) string { +func diskPathForLun(lun int64) string { return fmt.Sprintf("/dev/disk/azure/scsi1/lun%d", lun) } -func (da diskAttacher) WaitForDevice(ctx context.Context, lun int32) (device string, err error) { +func (da diskAttacher) WaitForDevice(ctx context.Context, lun int64) (device string, err error) { path := diskPathForLun(lun) for { diff --git a/builder/azure/chroot/diskattacher_other.go b/builder/azure/chroot/diskattacher_other.go index fc367496..9f728bd9 100644 --- a/builder/azure/chroot/diskattacher_other.go +++ b/builder/azure/chroot/diskattacher_other.go @@ -10,6 +10,6 @@ import ( "context" ) -func (da diskAttacher) WaitForDevice(ctx context.Context, lun int32) (device string, err error) { +func (da diskAttacher) WaitForDevice(ctx context.Context, lun int64) (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 c288080b..00000000 --- a/builder/azure/chroot/diskattacher_test.go +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package chroot - -import ( - "context" - "io/ioutil" - "strings" - "testing" - - "github.com/Azure/go-autorest/autorest/to" - - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2021-11-01/compute" - "github.com/hashicorp/packer-plugin-azure/builder/azure/common/client" - packersdk "github.com/hashicorp/packer-plugin-sdk/packer" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -// 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) - testDiskName := t.Name() - - errorBuffer := &strings.Builder{} - ui := &packersdk.BasicUi{ - Reader: strings.NewReader(""), - Writer: ioutil.Discard, - ErrorWriter: errorBuffer, - } - - da := NewDiskAttacher(azcli, ui) - - 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.DiskStateAttached { - 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.DiskStorageAccountTypesStandardLRS, - }, - DiskProperties: &compute.DiskProperties{ - DiskSizeGB: to.Int32Ptr(30), - CreationData: &compute.CreationData{CreateOption: compute.DiskCreateOptionEmpty}, - }, - }) - 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 index 58332098..750789cd 100644 --- a/builder/azure/chroot/diskset.go +++ b/builder/azure/chroot/diskset.go @@ -7,7 +7,7 @@ import "github.com/hashicorp/packer-plugin-azure/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 +type Diskset map[int64]client.Resource // OS return the OS disk resource ID or nil if it is not assigned func (ds Diskset) OS() *client.Resource { @@ -18,7 +18,7 @@ func (ds Diskset) OS() *client.Resource { } // Data return the data disk resource ID or nil if it is not assigned -func (ds Diskset) Data(lun int32) *client.Resource { +func (ds Diskset) Data(lun int64) *client.Resource { if r, ok := ds[lun]; ok { return &r } diff --git a/builder/azure/chroot/diskset_test.go b/builder/azure/chroot/diskset_test.go index 247c19ec..9d3e0022 100644 --- a/builder/azure/chroot/diskset_test.go +++ b/builder/azure/chroot/diskset_test.go @@ -13,7 +13,7 @@ func diskset(ids ...string) Diskset { if err != nil { panic(err) } - diskset[int32(i-1)] = r + diskset[int64(i-1)] = r } return diskset } diff --git a/builder/azure/chroot/shared_image_gallery_destination.go b/builder/azure/chroot/shared_image_gallery_destination.go index e84a55ed..0980a40d 100644 --- a/builder/azure/chroot/shared_image_gallery_destination.go +++ b/builder/azure/chroot/shared_image_gallery_destination.go @@ -29,7 +29,7 @@ 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"` + ReplicaCount int64 `mapstructure:"replicas"` // Storage account type: Standard_LRS or Standard_ZRS. Default: Standard_ZRS StorageAccountType string `mapstructure:"storage_account_type"` } diff --git a/builder/azure/chroot/shared_image_gallery_destination.hcl2spec.go b/builder/azure/chroot/shared_image_gallery_destination.hcl2spec.go index 3da1bc72..7ffd151d 100644 --- a/builder/azure/chroot/shared_image_gallery_destination.hcl2spec.go +++ b/builder/azure/chroot/shared_image_gallery_destination.hcl2spec.go @@ -46,7 +46,7 @@ func (*FlatSharedImageGalleryDestination) HCL2Spec() map[string]hcldec.Spec { // 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"` + ReplicaCount *int64 `mapstructure:"replicas" cty:"replicas" hcl:"replicas"` StorageAccountType *string `mapstructure:"storage_account_type" cty:"storage_account_type" hcl:"storage_account_type"` } diff --git a/builder/azure/chroot/step_attach_disk_test.go b/builder/azure/chroot/step_attach_disk_test.go index fcd01cd2..74522966 100644 --- a/builder/azure/chroot/step_attach_disk_test.go +++ b/builder/azure/chroot/step_attach_disk_test.go @@ -7,13 +7,10 @@ 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-azure/builder/azure/common/client" "github.com/hashicorp/packer-plugin-sdk/multistep" packersdk "github.com/hashicorp/packer-plugin-sdk/packer" @@ -70,15 +67,6 @@ func TestStepAttachDisk_Run(t *testing.T) { } } - 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 - }) - state := new(multistep.BasicStateBag) state.Put("azureclient", &client.AzureClientSetMock{}) state.Put("ui", ui) @@ -106,18 +94,18 @@ type fakeDiskAttacher struct { var _ DiskAttacher = &fakeDiskAttacher{} -func (da *fakeDiskAttacher) AttachDisk(ctx context.Context, disk string) (lun int32, err error) { +func (da *fakeDiskAttacher) AttachDisk(ctx context.Context, disk string) (lun int64, err error) { if da.attachError != nil { return 0, da.attachError } return 3, nil } -func (da *fakeDiskAttacher) DiskPathForLun(lun int32) string { +func (da *fakeDiskAttacher) DiskPathForLun(lun int64) string { panic("not implemented") } -func (da *fakeDiskAttacher) WaitForDevice(ctx context.Context, lun int32) (device string, err error) { +func (da *fakeDiskAttacher) WaitForDevice(ctx context.Context, lun int64) (device string, err error) { if da.waitForDeviceError != nil { return "", da.waitForDeviceError } diff --git a/builder/azure/chroot/step_create_image.go b/builder/azure/chroot/step_create_image.go index eaa81e0e..babffe26 100644 --- a/builder/azure/chroot/step_create_image.go +++ b/builder/azure/chroot/step_create_image.go @@ -9,9 +9,8 @@ import ( "log" "sort" - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2021-11-01/compute" - "github.com/Azure/go-autorest/autorest/azure" - "github.com/Azure/go-autorest/autorest/to" + "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-01/images" + "github.com/hashicorp/packer-plugin-azure/builder/azure/common" "github.com/hashicorp/packer-plugin-azure/builder/azure/common/client" "github.com/hashicorp/packer-plugin-sdk/multistep" packersdk "github.com/hashicorp/packer-plugin-sdk/packer" @@ -27,6 +26,13 @@ type StepCreateImage struct { DataDiskStorageAccountType string DataDiskCacheType string Location string + + create func(ctx context.Context, client client.AzureClientSet, id images.ImageId, image images.Image) error +} + +func NewStepCreateImage(step *StepCreateImage) *StepCreateImage { + step.create = step.createImage + return step } func (s *StepCreateImage) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { @@ -39,7 +45,7 @@ func (s *StepCreateImage) Run(ctx context.Context, state multistep.StateBag) mul s.ImageResourceID, diskResourceID)) - imageResource, err := azure.ParseResourceID(s.ImageResourceID) + imageResource, err := client.ParseResourceID(s.ImageResourceID) if err != nil { log.Printf("StepCreateImage.Run: error: %+v", err) @@ -50,55 +56,55 @@ func (s *StepCreateImage) Run(ctx context.Context, state multistep.StateBag) mul 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.OperatingSystemTypesLinux, - ManagedDisk: &compute.SubResource{ - ID: &diskResourceID, + storageAccountType := images.StorageAccountTypes(s.OSDiskStorageAccountType) + cacheingType := images.CachingTypes(s.OSDiskCacheType) + image := images.Image{ + Location: s.Location, + Properties: &images.ImageProperties{ + StorageProfile: &images.ImageStorageProfile{ + OsDisk: &images.ImageOSDisk{ + OsState: images.OperatingSystemStateTypes(s.ImageOSState), + OsType: images.OperatingSystemTypesLinux, + ManagedDisk: &images.SubResource{ + Id: &diskResourceID, }, - StorageAccountType: compute.StorageAccountTypes(s.OSDiskStorageAccountType), - Caching: compute.CachingTypes(s.OSDiskCacheType), + StorageAccountType: &storageAccountType, + Caching: &cacheingType, }, - // DataDisks: nil, - // ZoneResilient: nil, }, }, - // Tags: nil, } - var datadisks []compute.ImageDataDisk + var datadisks []images.ImageDataDisk + if len(diskset) > 0 { + storageAccountType = images.StorageAccountTypes(s.DataDiskStorageAccountType) + cacheingType = images.CachingTypes(s.DataDiskStorageAccountType) + } 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), + datadisks = append(datadisks, images.ImageDataDisk{ + Lun: lun, + ManagedDisk: &images.SubResource{Id: common.StringPtr(resource.String())}, + StorageAccountType: &storageAccountType, + Caching: &cacheingType, }) } } if datadisks != nil { sort.Slice(datadisks, func(i, j int) bool { - return *datadisks[i].Lun < *datadisks[j].Lun + return datadisks[i].Lun < datadisks[j].Lun }) - image.ImageProperties.StorageProfile.DataDisks = &datadisks + image.Properties.StorageProfile.DataDisks = &datadisks } - f, err := azcli.ImagesClient().CreateOrUpdate( + id := images.NewImageID(azcli.SubscriptionID(), imageResource.ResourceGroup, imageResource.ResourceName.String()) + err = s.create( ctx, - imageResource.ResourceGroup, - imageResource.ResourceName, + azcli, + id, 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( @@ -107,9 +113,13 @@ func (s *StepCreateImage) Run(ctx context.Context, state multistep.StateBag) mul ui.Error(err.Error()) return multistep.ActionHalt } - log.Printf("Image creation complete: %s", f.Status()) + log.Printf("Image creation complete") return multistep.ActionContinue } +func (s *StepCreateImage) createImage(ctx context.Context, client client.AzureClientSet, id images.ImageId, image images.Image) error { + return client.ImagesClient().CreateOrUpdateThenPoll(ctx, id, image) +} + 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 index faa49ec1..6e06c866 100644 --- a/builder/azure/chroot/step_create_image_test.go +++ b/builder/azure/chroot/step_create_image_test.go @@ -5,21 +5,20 @@ package chroot import ( "context" - "io/ioutil" - "net/http" + "fmt" "reflect" - "regexp" "testing" + "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-01/images" "github.com/hashicorp/packer-plugin-azure/builder/azure/common/client" "github.com/hashicorp/packer-plugin-sdk/multistep" packersdk "github.com/hashicorp/packer-plugin-sdk/packer" - - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2021-11-01/compute" - "github.com/Azure/go-autorest/autorest" ) func TestStepCreateImage_Run(t *testing.T) { + subscriptionID := "12345" + resourceGroup := "group1" + imageName := "myImage" type fields struct { ImageResourceID string ImageOSState string @@ -30,16 +29,15 @@ func TestStepCreateImage_Run(t *testing.T) { Location string } tests := []struct { - name string - fields fields - diskset Diskset - want multistep.StepAction - wantPutBody string + name string + fields fields + diskset Diskset + want multistep.StepAction }{ { name: "happy path", fields: fields{ - ImageResourceID: "/subscriptions/12345/resourceGroups/group1/providers/Microsoft.Compute/images/myImage", + ImageResourceID: fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Compute/images/%s", subscriptionID, resourceGroup, imageName), Location: "location1", OSDiskStorageAccountType: "Standard_LRS", OSDiskCacheType: "ReadWrite", @@ -52,77 +50,17 @@ func TestStepCreateImage_Run(t *testing.T) { "/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("azureclient", &client.AzureClientSetMock{SubscriptionIDMock: subscriptionID}) state.Put("ui", packersdk.TestUi(t)) state.Put(stateBagKey_Diskset, tt.diskset) - + expectedImageID := images.NewImageID(subscriptionID, resourceGroup, imageName) t.Run(tt.name, func(t *testing.T) { + var actualImageID images.ImageId + var actualImage images.Image s := &StepCreateImage{ ImageResourceID: tt.fields.ImageResourceID, ImageOSState: tt.fields.ImageOSState, @@ -131,10 +69,28 @@ func TestStepCreateImage_Run(t *testing.T) { DataDiskStorageAccountType: tt.fields.DataDiskStorageAccountType, DataDiskCacheType: tt.fields.DataDiskCacheType, Location: tt.fields.Location, + create: func(ctx context.Context, client client.AzureClientSet, id images.ImageId, image images.Image) error { + actualImageID = id + actualImage = image + return nil + }, } if got := s.Run(context.TODO(), state); !reflect.DeepEqual(got, tt.want) { t.Errorf("StepCreateImage.Run() = %v, want %v", got, tt.want) } + if expectedImageID != actualImageID { + t.Fatalf("Expected StepCreateImage.create called with image ID %s got image ID %s", expectedImageID, actualImageID) + } + + if len(*actualImage.Properties.StorageProfile.DataDisks) != 3 { + t.Fatalf("Expected 3 data disks attatched to created image got %d", len(*actualImage.Properties.StorageProfile.DataDisks)) + } + if actualImage.Properties.StorageProfile.OsDisk.OsType != "Linux" { + t.Fatalf("Expected actual image to be Linux, got %s", actualImage.Properties.StorageProfile.OsDisk.OsType) + } + if actualImage.Location != tt.fields.Location { + t.Fatalf("Expected %s location got %s location", tt.fields.Location, actualImage.Location) + } }) } } diff --git a/builder/azure/chroot/step_create_new_diskset.go b/builder/azure/chroot/step_create_new_diskset.go index db2ea2b1..ea5f77e8 100644 --- a/builder/azure/chroot/step_create_new_diskset.go +++ b/builder/azure/chroot/step_create_new_diskset.go @@ -8,21 +8,21 @@ import ( "fmt" "log" "strings" - "time" + "github.com/hashicorp/go-azure-helpers/polling" "github.com/hashicorp/packer-plugin-azure/builder/azure/common/client" "github.com/hashicorp/packer-plugin-sdk/multistep" packersdk "github.com/hashicorp/packer-plugin-sdk/packer" - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2021-11-01/compute" - "github.com/Azure/go-autorest/autorest/to" + "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-02/disks" + "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-03/galleryimageversions" ) var _ multistep.Step = &StepCreateNewDiskset{} type StepCreateNewDiskset struct { OSDiskID string // Disk ID - OSDiskSizeGB int32 // optional, ignored if 0 + OSDiskSizeGB int64 // optional, ignored if 0 OSDiskStorageAccountType string // from compute.DiskStorageAccountTypes DataDiskStorageAccountType string // from compute.DiskStorageAccountTypes @@ -43,8 +43,16 @@ type StepCreateNewDiskset struct { Location string SkipCleanup bool + + getVersion func(context.Context, client.AzureClientSet, galleryimageversions.ImageVersionId) (*galleryimageversions.GalleryImageVersion, error) + create func(context.Context, client.AzureClientSet, disks.DiskId, disks.Disk) (polling.LongRunningPoller, error) } +func NewStepCreateNewDiskset(step *StepCreateNewDiskset) *StepCreateNewDiskset { + step.getVersion = step.getSharedImageGalleryVersion + step.create = step.createDiskset + return step +} func (s *StepCreateNewDiskset) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { azcli := state.Get("azureclient").(client.AzureClientSet) ui := state.Get("ui").(packersdk.Ui) @@ -72,7 +80,8 @@ func (s *StepCreateNewDiskset) Run(ctx context.Context, state multistep.StateBag disk := s.getOSDiskDefinition(azcli.SubscriptionID()) // Initiate disk creation - f, err := azcli.DisksClient().CreateOrUpdate(ctx, osDisk.ResourceGroup, osDisk.ResourceName.String(), disk) + diskId := disks.NewDiskID(azcli.SubscriptionID(), osDisk.ResourceGroup, osDisk.ResourceName.String()) + response, err := s.create(ctx, azcli, diskId, disk) if err != nil { return errorMessage("Failed to initiate resource creation: %q", osDisk) } @@ -82,9 +91,9 @@ func (s *StepCreateNewDiskset) Run(ctx context.Context, state multistep.StateBag type Future struct { client.Resource - compute.DisksCreateOrUpdateFuture + polling.LongRunningPoller } - futures := []Future{{osDisk, f}} + futures := []Future{{osDisk, response}} if s.SourceImageResourceID != "" { // retrieve image to see if there are any datadisks @@ -97,32 +106,28 @@ func (s *StepCreateNewDiskset) Run(ctx context.Context, state multistep.StateBag "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], "") + galleryImageVersionId := galleryimageversions.NewImageVersionID(azcli.SubscriptionID(), + imageID.ResourceGroup, imageID.ResourceName[0], imageID.ResourceName[1], imageID.ResourceName[2]) + image, err := s.getVersion(ctx, azcli, galleryImageVersionId) 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 image.Properties != nil && + image.Properties.StorageProfile.DataDiskImages != nil { + for _, ddi := range *image.Properties.StorageProfile.DataDiskImages { + 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) + disk := s.getDatadiskDefinitionFromImage(ddi.Lun) // Initiate disk creation - f, err := azcli.DisksClient().CreateOrUpdate(ctx, datadiskID.ResourceGroup, datadiskID.ResourceName.String(), disk) + diskId := disks.NewDiskID(azcli.SubscriptionID(), datadiskID.ResourceGroup, datadiskID.ResourceName.String()) + f, err := s.create(ctx, azcli, diskId, 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 + 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)) @@ -135,12 +140,9 @@ func (s *StepCreateNewDiskset) Run(ctx context.Context, state multistep.StateBag // 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) + error := f.LongRunningPoller.PollUntilDone() + if error != nil { + return errorMessage("Failed to create resource %q error %s", f.Resource, error) } ui.Say(fmt.Sprintf("Disk %q created", f.Resource)) } @@ -148,27 +150,30 @@ func (s *StepCreateNewDiskset) Run(ctx context.Context, state multistep.StateBag 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{}, +func (s StepCreateNewDiskset) getOSDiskDefinition(subscriptionID string) disks.Disk { + osType := disks.OperatingSystemTypesLinux + disk := disks.Disk{ + Location: s.Location, + Properties: &disks.DiskProperties{ + OsType: &osType, + CreationData: disks.CreationData{}, }, } if s.OSDiskStorageAccountType != "" { - disk.Sku = &compute.DiskSku{ - Name: compute.DiskStorageAccountTypes(s.OSDiskStorageAccountType), + hashiDiskSkuName := disks.DiskStorageAccountTypes(s.OSDiskStorageAccountType) + disk.Sku = &disks.DiskSku{ + Name: &hashiDiskSkuName, } } if s.HyperVGeneration != "" { - disk.DiskProperties.HyperVGeneration = compute.HyperVGeneration(s.HyperVGeneration) + hyperVGeneration := disks.HyperVGeneration(s.HyperVGeneration) + disk.Properties.HyperVGeneration = &hyperVGeneration } if s.OSDiskSizeGB > 0 { - disk.DiskProperties.DiskSizeGB = to.Int32Ptr(s.OSDiskSizeGB) + disk.Properties.DiskSizeGB = &s.OSDiskSizeGB } switch { @@ -176,46 +181,67 @@ func (s StepCreateNewDiskset) getOSDiskDefinition(subscriptionID string) compute imageID := 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) - disk.CreationData.CreateOption = compute.DiskCreateOptionFromImage - disk.CreationData.ImageReference = &compute.ImageDiskReference{ - ID: to.StringPtr(imageID), + disk.Properties.CreationData.CreateOption = disks.DiskCreateOptionFromImage + disk.Properties.CreationData.ImageReference = &disks.ImageDiskReference{ + Id: &imageID, } case s.SourceOSDiskResourceID != "": - disk.CreationData.CreateOption = compute.DiskCreateOptionCopy - disk.CreationData.SourceResourceID = to.StringPtr(s.SourceOSDiskResourceID) + disk.Properties.CreationData.CreateOption = disks.DiskCreateOptionCopy + disk.Properties.CreationData.SourceResourceId = &s.SourceOSDiskResourceID case s.SourceImageResourceID != "": - disk.CreationData.CreateOption = compute.DiskCreateOptionFromImage - disk.CreationData.GalleryImageReference = &compute.ImageDiskReference{ - ID: to.StringPtr(s.SourceImageResourceID), + disk.Properties.CreationData.CreateOption = disks.DiskCreateOptionFromImage + disk.Properties.CreationData.GalleryImageReference = &disks.ImageDiskReference{ + Id: &s.SourceImageResourceID, } default: - disk.CreationData.CreateOption = compute.DiskCreateOptionEmpty + disk.Properties.CreationData.CreateOption = disks.DiskCreateOptionEmpty } return disk } -func (s StepCreateNewDiskset) getDatadiskDefinitionFromImage(lun int32) compute.Disk { - disk := compute.Disk{ - Location: to.StringPtr(s.Location), - DiskProperties: &compute.DiskProperties{ - CreationData: &compute.CreationData{}, +func (s StepCreateNewDiskset) getDatadiskDefinitionFromImage(lun int64) disks.Disk { + disk := disks.Disk{ + Location: s.Location, + Properties: &disks.DiskProperties{ + CreationData: disks.CreationData{}, }, } - disk.CreationData.CreateOption = compute.DiskCreateOptionFromImage - disk.CreationData.GalleryImageReference = &compute.ImageDiskReference{ - ID: to.StringPtr(s.SourceImageResourceID), - Lun: to.Int32Ptr(lun), + disk.Properties.CreationData.CreateOption = disks.DiskCreateOptionFromImage + disk.Properties.CreationData.GalleryImageReference = &disks.ImageDiskReference{ + Id: &s.SourceImageResourceID, + Lun: &lun, } + diskSkuName := disks.DiskStorageAccountTypes(s.DataDiskStorageAccountType) if s.DataDiskStorageAccountType != "" { - disk.Sku = &compute.DiskSku{ - Name: compute.DiskStorageAccountTypes(s.DataDiskStorageAccountType), + disk.Sku = &disks.DiskSku{ + Name: &diskSkuName, } } return disk } +func (s *StepCreateNewDiskset) createDiskset(ctx context.Context, azcli client.AzureClientSet, id disks.DiskId, disk disks.Disk) (polling.LongRunningPoller, error) { + f, err := azcli.DisksClient().CreateOrUpdate(ctx, id, disk) + if err != nil { + return polling.LongRunningPoller{}, err + } + return f.Poller, nil +} + +func (s *StepCreateNewDiskset) getSharedImageGalleryVersion(ctx context.Context, azclient client.AzureClientSet, id galleryimageversions.ImageVersionId) (*galleryimageversions.GalleryImageVersion, error) { + + imageVersionResult, err := azclient.GalleryImageVersionsClient().Get(ctx, id, galleryimageversions.DefaultGetOperationOptions()) + if err != nil { + return nil, err + } + if imageVersionResult.Model == nil { + return nil, fmt.Errorf("SDK returned empty model") + } + return imageVersionResult.Model, nil +} + func (s *StepCreateNewDiskset) Cleanup(state multistep.StateBag) { if !s.SkipCleanup { azcli := state.Get("azureclient").(client.AzureClientSet) @@ -231,10 +257,8 @@ func (s *StepCreateNewDiskset) Cleanup(state multistep.StateBag) { 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()) - } + diskID := disks.NewDiskID(azcli.SubscriptionID(), d.ResourceGroup, d.ResourceName.String()) + err = azcli.DisksClient().DeleteThenPoll(context.TODO(), diskID) 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 index 581723dd..b24ef490 100644 --- a/builder/azure/chroot/step_create_new_diskset_test.go +++ b/builder/azure/chroot/step_create_new_diskset_test.go @@ -5,39 +5,62 @@ package chroot import ( "context" - "io/ioutil" "net/http" "reflect" - "regexp" - "strings" "testing" + "github.com/Azure/go-autorest/autorest" + "github.com/google/go-cmp/cmp" + "github.com/hashicorp/go-azure-helpers/polling" + "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-02/disks" + "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-03/galleryimageversions" + "github.com/hashicorp/packer-plugin-azure/builder/azure/common" "github.com/hashicorp/packer-plugin-azure/builder/azure/common/client" "github.com/hashicorp/packer-plugin-sdk/multistep" packersdk "github.com/hashicorp/packer-plugin-sdk/packer" - - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2021-11-01/compute" - "github.com/Azure/go-autorest/autorest" ) func TestStepCreateNewDisk_Run(t *testing.T) { + osType := disks.OperatingSystemTypesLinux + hyperVGeneration := disks.HyperVGenerationVOne + premiumLRS := disks.DiskStorageAccountTypesPremiumLRS + standardLRS := disks.DiskStorageAccountTypesStandardLRS + tests := []struct { name string fields StepCreateNewDiskset expectedPutDiskBodies []string want multistep.StepAction verifyDiskset *Diskset + disks []disks.Disk }{ { name: "from disk", fields: StepCreateNewDiskset{ OSDiskID: "/subscriptions/SubscriptionID/resourcegroups/ResourceGroupName/providers/Microsoft.Compute/disks/TemporaryOSDiskName", OSDiskSizeGB: 42, - OSDiskStorageAccountType: string(compute.StorageAccountTypesPremiumLRS), - HyperVGeneration: string(compute.HyperVGenerationTypesV1), + OSDiskStorageAccountType: string(disks.DiskStorageAccountTypesStandardLRS), + HyperVGeneration: string(disks.HyperVGenerationVOne), Location: "westus", SourceOSDiskResourceID: "SourceDisk", }, + disks: []disks.Disk{ + { + Location: "westus", + Sku: &disks.DiskSku{ + Name: &standardLRS, + }, + Properties: &disks.DiskProperties{ + HyperVGeneration: &hyperVGeneration, + OsType: &osType, + CreationData: disks.CreationData{ + CreateOption: disks.DiskCreateOptionCopy, + SourceResourceId: common.StringPtr("SourceDisk"), + }, + DiskSizeGB: common.Int64Ptr(42), + }, + }, + }, expectedPutDiskBodies: []string{` { "location": "westus", @@ -61,8 +84,8 @@ func TestStepCreateNewDisk_Run(t *testing.T) { name: "from platform image", fields: StepCreateNewDiskset{ OSDiskID: "/subscriptions/SubscriptionID/resourcegroups/ResourceGroupName/providers/Microsoft.Compute/disks/TemporaryOSDiskName", - OSDiskStorageAccountType: string(compute.StorageAccountTypeStandardLRS), - HyperVGeneration: string(compute.HyperVGenerationTypesV1), + OSDiskStorageAccountType: string(disks.DiskStorageAccountTypesStandardLRS), + HyperVGeneration: string(disks.HyperVGenerationVOne), Location: "westus", SourcePlatformImage: &client.PlatformImage{ Publisher: "Microsoft", @@ -71,6 +94,24 @@ func TestStepCreateNewDisk_Run(t *testing.T) { Version: "2016.1.4", }, }, + disks: []disks.Disk{ + { + Location: "westus", + Sku: &disks.DiskSku{ + Name: &standardLRS, + }, + Properties: &disks.DiskProperties{ + HyperVGeneration: &hyperVGeneration, + OsType: &osType, + CreationData: disks.CreationData{ + CreateOption: disks.DiskCreateOptionFromImage, + ImageReference: &disks.ImageDiskReference{ + Id: common.StringPtr("/subscriptions/SubscriptionID/providers/Microsoft.Compute/locations/westus/publishers/Microsoft/artifacttypes/vmimage/offers/Windows/skus/2016-DataCenter/versions/2016.1.4"), + }, + }, + }, + }, + }, expectedPutDiskBodies: []string{` { "location": "westus", @@ -95,76 +136,78 @@ func TestStepCreateNewDisk_Run(t *testing.T) { name: "from shared image", fields: StepCreateNewDiskset{ OSDiskID: "/subscriptions/SubscriptionID/resourcegroups/ResourceGroupName/providers/Microsoft.Compute/disks/TemporaryOSDiskName", - OSDiskStorageAccountType: string(compute.StorageAccountTypeStandardLRS), - DataDiskStorageAccountType: string(compute.StorageAccountTypesPremiumLRS), + OSDiskStorageAccountType: string(disks.DiskStorageAccountTypesStandardLRS), + DataDiskStorageAccountType: string(disks.DiskStorageAccountTypesPremiumLRS), DataDiskIDPrefix: "/subscriptions/SubscriptionID/resourcegroups/ResourceGroupName/providers/Microsoft.Compute/disks/TemporaryDataDisk-", - HyperVGeneration: string(compute.HyperVGenerationTypesV1), + HyperVGeneration: string(disks.HyperVGenerationVOne), Location: "westus", SourceImageResourceID: "/subscriptions/SubscriptionID/resourcegroups/imagegroup/providers/Microsoft.Compute/galleries/MyGallery/images/MyImage/versions/1.2.3", }, - expectedPutDiskBodies: []string{` + disks: []disks.Disk{ { - "location": "westus", - "properties": { - "creationData": { - "createOption":"FromImage", - "galleryImageReference": { - "id":"/subscriptions/SubscriptionID/resourcegroups/imagegroup/providers/Microsoft.Compute/galleries/MyGallery/images/MyImage/versions/1.2.3" - } + Location: "westus", + Properties: &disks.DiskProperties{ + CreationData: disks.CreationData{ + CreateOption: disks.DiskCreateOptionFromImage, + GalleryImageReference: &disks.ImageDiskReference{ + Id: common.StringPtr("/subscriptions/SubscriptionID/resourcegroups/imagegroup/providers/Microsoft.Compute/galleries/MyGallery/images/MyImage/versions/1.2.3"), + }, }, - "hyperVGeneration": "V1", - "osType": "Linux" + HyperVGeneration: &hyperVGeneration, + OsType: &osType, }, - "sku": { - "name": "Standard_LRS" - } - }`, ` + Sku: &disks.DiskSku{ + Name: &standardLRS, + }, + }, { - "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 - } - } + Location: "westus", + Properties: &disks.DiskProperties{ + CreationData: disks.CreationData{ + CreateOption: disks.DiskCreateOptionFromImage, + GalleryImageReference: &disks.ImageDiskReference{ + Id: common.StringPtr("/subscriptions/SubscriptionID/resourcegroups/imagegroup/providers/Microsoft.Compute/galleries/MyGallery/images/MyImage/versions/1.2.3"), + Lun: common.Int64Ptr(5), + }, + }, }, - "sku": { - "name": "Premium_LRS" - } - }`, ` + Sku: &disks.DiskSku{ + Name: &premiumLRS, + }, + }, { - "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 - } - } + Location: "westus", + Properties: &disks.DiskProperties{ + CreationData: disks.CreationData{ + CreateOption: disks.DiskCreateOptionFromImage, + GalleryImageReference: &disks.ImageDiskReference{ + Id: common.StringPtr("/subscriptions/SubscriptionID/resourcegroups/imagegroup/providers/Microsoft.Compute/galleries/MyGallery/images/MyImage/versions/1.2.3"), + Lun: common.Int64Ptr(9), + }, + }, }, - "sku": { - "name": "Premium_LRS" - } - }`, ` + Sku: &disks.DiskSku{ + Name: &premiumLRS, + }, + }, { - "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 - } - } + Location: "westus", + Properties: &disks.DiskProperties{ + CreationData: disks.CreationData{ + CreateOption: disks.DiskCreateOptionFromImage, + GalleryImageReference: &disks.ImageDiskReference{ + Id: common.StringPtr("/subscriptions/SubscriptionID/resourcegroups/imagegroup/providers/Microsoft.Compute/galleries/MyGallery/images/MyImage/versions/1.2.3"), + Lun: common.Int64Ptr(3), + }, + }, }, - "sku": { - "name": "Premium_LRS" - } - }`}, + Sku: &disks.DiskSku{ + Name: &premiumLRS, + }, + }, + }, + want: multistep.ActionContinue, verifyDiskset: &Diskset{ -1: resource("/subscriptions/SubscriptionID/resourceGroups/ResourceGroupName/providers/Microsoft.Compute/disks/TemporaryOSDiskName"), @@ -176,56 +219,53 @@ func TestStepCreateNewDisk_Run(t *testing.T) { } 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, + s := StepCreateNewDiskset{ + OSDiskID: tt.fields.OSDiskID, + OSDiskSizeGB: tt.fields.OSDiskSizeGB, + OSDiskStorageAccountType: tt.fields.OSDiskStorageAccountType, + DataDiskStorageAccountType: tt.fields.DataDiskStorageAccountType, + DataDiskIDPrefix: tt.fields.DataDiskIDPrefix, + HyperVGeneration: tt.fields.HyperVGeneration, + Location: tt.fields.Location, + SourceOSDiskResourceID: tt.fields.SourceOSDiskResourceID, + SourceImageResourceID: tt.fields.SourceImageResourceID, + SourcePlatformImage: tt.fields.SourcePlatformImage, + getVersion: func(ctx context.Context, acs client.AzureClientSet, id galleryimageversions.ImageVersionId) (*galleryimageversions.GalleryImageVersion, error) { + return &galleryimageversions.GalleryImageVersion{ + Properties: &galleryimageversions.GalleryImageVersionProperties{ + StorageProfile: galleryimageversions.GalleryImageVersionStorageProfile{ + DataDiskImages: &[]galleryimageversions.GalleryDataDiskImage{ + { + Lun: 5, + }, + { + Lun: 9, + }, + { + Lun: 3, + }, + }, + }, + }, }, nil - } - return &http.Response{ - Request: r, - Status: "Unexpected request", - StatusCode: 500, - }, nil - }) + }, + create: func(ctx context.Context, acs client.AzureClientSet, id disks.DiskId, disk disks.Disk) (polling.LongRunningPoller, error) { + if diff := cmp.Diff(disk, tt.disks[bodyCount]); diff != "" { + t.Fatalf("unexpected disk for call %d diff %s", bodyCount+1, diff) + } + bodyCount++ + future, err := polling.NewPollerFromResponse(context.TODO(), &http.Response{Request: &http.Request{Method: http.MethodDelete}, StatusCode: 200}, autorest.Client{}, "DELETE") + if err != nil { + t.Fatalf("%s", err) + } + return future, nil + }, + } state := new(multistep.BasicStateBag) state.Put("azureclient", &client.AzureClientSetMock{ - SubscriptionIDMock: "SubscriptionID", - DisksClientMock: m, - GalleryImageVersionsClientMock: giv, + SubscriptionIDMock: "SubscriptionID", }) state.Put("ui", packersdk.TestUi(t)) diff --git a/builder/azure/chroot/step_create_shared_image_version.go b/builder/azure/chroot/step_create_shared_image_version.go index e5b2e56b..d3fb8c40 100644 --- a/builder/azure/chroot/step_create_shared_image_version.go +++ b/builder/azure/chroot/step_create_shared_image_version.go @@ -8,10 +8,9 @@ import ( "fmt" "log" "sort" - "time" - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2021-11-01/compute" - "github.com/Azure/go-autorest/autorest/to" + "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-03/galleryimageversions" + "github.com/hashicorp/packer-plugin-azure/builder/azure/common" "github.com/hashicorp/packer-plugin-azure/builder/azure/common/client" "github.com/hashicorp/packer-plugin-sdk/multistep" packersdk "github.com/hashicorp/packer-plugin-sdk/packer" @@ -22,6 +21,13 @@ type StepCreateSharedImageVersion struct { OSDiskCacheType string DataDiskCacheType string Location string + + create func(context.Context, client.AzureClientSet, galleryimageversions.ImageVersionId, galleryimageversions.GalleryImageVersion) error +} + +func NewStepCreateSharedImageVersion(step *StepCreateSharedImageVersion) *StepCreateSharedImageVersion { + step.create = step.createImageVersion + return step } func (s *StepCreateSharedImageVersion) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { @@ -33,68 +39,69 @@ func (s *StepCreateSharedImageVersion) Run(ctx context.Context, state multistep. s.Destination.ResourceID(azcli.SubscriptionID()), snapshotset.OS())) - var targetRegions []compute.TargetRegion + var targetRegions []galleryimageversions.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), + trStorageAccountType := galleryimageversions.StorageAccountType(tr.StorageAccountType) + apiObject := galleryimageversions.TargetRegion{ + Name: tr.Name, + RegionalReplicaCount: &tr.ReplicaCount, + StorageAccountType: &trStorageAccountType, } 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), + osDiskSource := snapshotset.OS().String() + hostCaching := galleryimageversions.HostCaching(s.OSDiskCacheType) + imageVersion := galleryimageversions.GalleryImageVersion{ + Location: s.Location, + Properties: &galleryimageversions.GalleryImageVersionProperties{ + StorageProfile: galleryimageversions.GalleryImageVersionStorageProfile{ + OsDiskImage: &galleryimageversions.GalleryDiskImage{ + Source: &galleryimageversions.GalleryDiskImageSource{Id: &osDiskSource}, + HostCaching: &hostCaching, }, }, - PublishingProfile: &compute.GalleryImageVersionPublishingProfile{ + PublishingProfile: &galleryimageversions.GalleryArtifactPublishingProfileBase{ TargetRegions: &targetRegions, - ExcludeFromLatest: to.BoolPtr(s.Destination.ExcludeFromLatest), + ExcludeFromLatest: common.BoolPtr(s.Destination.ExcludeFromLatest), }, }, } - var datadisks []compute.GalleryDataDiskImage + var datadisks []galleryimageversions.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), + hostCaching := galleryimageversions.HostCaching(s.DataDiskCacheType) + datadisks = append(datadisks, galleryimageversions.GalleryDataDiskImage{ + Lun: lun, + Source: &galleryimageversions.GalleryDiskImageSource{Id: common.StringPtr(resource.String())}, + HostCaching: &hostCaching, }) } } if datadisks != nil { // sort by lun sort.Slice(datadisks, func(i, j int) bool { - return *datadisks[i].Lun < *datadisks[j].Lun + return datadisks[i].Lun < datadisks[j].Lun }) - imageVersion.GalleryImageVersionProperties.StorageProfile.DataDiskImages = &datadisks + imageVersion.Properties.StorageProfile.DataDiskImages = &datadisks } - f, err := azcli.GalleryImageVersionsClient().CreateOrUpdate( - ctx, + galleryImageVersionID := galleryimageversions.NewImageVersionID( + azcli.SubscriptionID(), s.Destination.ResourceGroup, s.Destination.GalleryName, s.Destination.ImageName, s.Destination.ImageVersion, + ) + err := s.create( + ctx, + azcli, + galleryImageVersionID, 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( @@ -103,9 +110,16 @@ func (s *StepCreateSharedImageVersion) Run(ctx context.Context, state multistep. ui.Error(err.Error()) return multistep.ActionHalt } - log.Printf("Image creation complete: %s", f.Status()) + log.Printf("Image creation complete") return multistep.ActionContinue } +func (s *StepCreateSharedImageVersion) createImageVersion(ctx context.Context, azcli client.AzureClientSet, galleryImageVersionID galleryimageversions.ImageVersionId, imageVersion galleryimageversions.GalleryImageVersion) error { + return azcli.GalleryImageVersionsClient().CreateOrUpdateThenPoll( + ctx, + galleryImageVersionID, + imageVersion) +} + 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 index e279d4de..87228fad 100644 --- a/builder/azure/chroot/step_create_shared_image_version_test.go +++ b/builder/azure/chroot/step_create_shared_image_version_test.go @@ -5,21 +5,21 @@ package chroot import ( "context" - "io/ioutil" - "net/http" - "reflect" - "regexp" "testing" + "github.com/google/go-cmp/cmp" + "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-03/galleryimageversions" + "github.com/hashicorp/packer-plugin-azure/builder/azure/common" "github.com/hashicorp/packer-plugin-azure/builder/azure/common/client" "github.com/hashicorp/packer-plugin-sdk/multistep" packersdk "github.com/hashicorp/packer-plugin-sdk/packer" - - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2021-11-01/compute" - "github.com/Azure/go-autorest/autorest" ) func TestStepCreateSharedImageVersion_Run(t *testing.T) { + standardZRSStorageType := galleryimageversions.StorageAccountTypeStandardZRS + hostCacheingRW := galleryimageversions.HostCachingReadWrite + hostCacheingNone := galleryimageversions.HostCachingNone + subscriptionID := "12345" type fields struct { Destination SharedImageGalleryDestination OSDiskCacheType string @@ -27,11 +27,12 @@ func TestStepCreateSharedImageVersion_Run(t *testing.T) { Location string } tests := []struct { - name string - fields fields - snapshotset Diskset - want multistep.StepAction - expectedPutBody string + name string + fields fields + snapshotset Diskset + want multistep.StepAction + expectedImageVersion galleryimageversions.GalleryImageVersion + expectedImageId galleryimageversions.ImageVersionId }{ { name: "happy path", @@ -59,88 +60,93 @@ func TestStepCreateSharedImageVersion_Run(t *testing.T) { "/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": { - "excludeFromLatest": true, - "targetRegions": [ + expectedImageId: galleryimageversions.NewImageVersionID( + subscriptionID, + "ResourceGroup", + "GalleryName", + "ImageName", + "0.1.2", + ), + expectedImageVersion: galleryimageversions.GalleryImageVersion{ + Location: "region2", + Properties: &galleryimageversions.GalleryImageVersionProperties{ + PublishingProfile: &galleryimageversions.GalleryArtifactPublishingProfileBase{ + ExcludeFromLatest: common.BoolPtr(true), + TargetRegions: &[]galleryimageversions.TargetRegion{ { - "name": "region1", - "regionalReplicaCount": 5, - "storageAccountType": "Standard_ZRS" - } - ] + Name: "region1", + RegionalReplicaCount: common.Int64Ptr(5), + StorageAccountType: &standardZRSStorageType, + }, + }, }, - "storageProfile": { - "osDiskImage": { - "hostCaching": "ReadWrite", - "source": { - "id": "/subscriptions/12345/resourceGroups/group1/providers/Microsoft.Compute/snapshots/osdisksnapshot" - } + StorageProfile: galleryimageversions.GalleryImageVersionStorageProfile{ + OsDiskImage: &galleryimageversions.GalleryDiskImage{ + Source: &galleryimageversions.GalleryDiskImageSource{ + Id: common.StringPtr("/subscriptions/12345/resourceGroups/group1/providers/Microsoft.Compute/snapshots/osdisksnapshot"), + }, + HostCaching: &hostCacheingRW, }, - "dataDiskImages": [ + DataDiskImages: &[]galleryimageversions.GalleryDataDiskImage{ { - "hostCaching": "None", - "lun": 0, - "source": { - "id": "/subscriptions/12345/resourceGroups/group1/providers/Microsoft.Compute/snapshots/datadisksnapshot0" - } + HostCaching: &hostCacheingNone, + Lun: 0, + Source: &galleryimageversions.GalleryDiskImageSource{ + Id: common.StringPtr("/subscriptions/12345/resourceGroups/group1/providers/Microsoft.Compute/snapshots/datadisksnapshot0"), + }, }, { - "hostCaching": "None", - "lun": 1, - "source": { - "id": "/subscriptions/12345/resourceGroups/group1/providers/Microsoft.Compute/snapshots/datadisksnapshot1" - } + HostCaching: &hostCacheingNone, + Lun: 1, + Source: &galleryimageversions.GalleryDiskImageSource{ + Id: common.StringPtr("/subscriptions/12345/resourceGroups/group1/providers/Microsoft.Compute/snapshots/datadisksnapshot1"), + }, }, { - "hostCaching": "None", - "lun": 2, - "source": { - "id": "/subscriptions/12345/resourceGroups/group1/providers/Microsoft.Compute/snapshots/datadisksnapshot2" - } - } - ] - } - } - }`, + HostCaching: &hostCacheingNone, + Lun: 2, + Source: &galleryimageversions.GalleryDiskImageSource{ + Id: common.StringPtr("/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, + SubscriptionIDMock: subscriptionID, }) state.Put("ui", packersdk.TestUi(t)) state.Put(stateBagKey_Snapshotset, tt.snapshotset) t.Run(tt.name, func(t *testing.T) { + var actualID galleryimageversions.ImageVersionId + var actualImageVersion galleryimageversions.GalleryImageVersion s := &StepCreateSharedImageVersion{ Destination: tt.fields.Destination, OSDiskCacheType: tt.fields.OSDiskCacheType, DataDiskCacheType: tt.fields.DataDiskCacheType, Location: tt.fields.Location, + create: func(ctx context.Context, azcli client.AzureClientSet, id galleryimageversions.ImageVersionId, imageVersion galleryimageversions.GalleryImageVersion) error { + actualID = id + actualImageVersion = imageVersion + return nil + }, + } + + action := s.Run(context.TODO(), state) + if action != multistep.ActionContinue { + t.Fatalf("Expected ActionContinue got %s", action) + } + if diff := cmp.Diff(actualImageVersion, tt.expectedImageVersion); diff != "" { + t.Fatalf("unexpected image version %s", diff) } - if got := s.Run(context.TODO(), state); !reflect.DeepEqual(got, tt.want) { - t.Errorf("StepCreateSharedImageVersion.Run() = %v, want %v", got, tt.want) + if actualID != tt.expectedImageId { + t.Fatalf("Expected image ID %+v got %+v", tt.expectedImageId, actualID) } }) } diff --git a/builder/azure/chroot/step_create_snapshotset.go b/builder/azure/chroot/step_create_snapshotset.go index df235d94..79f650ea 100644 --- a/builder/azure/chroot/step_create_snapshotset.go +++ b/builder/azure/chroot/step_create_snapshotset.go @@ -8,14 +8,12 @@ import ( "fmt" "log" "strings" - "time" + "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-02/snapshots" + "github.com/hashicorp/packer-plugin-azure/builder/azure/common" "github.com/hashicorp/packer-plugin-azure/builder/azure/common/client" "github.com/hashicorp/packer-plugin-sdk/multistep" packersdk "github.com/hashicorp/packer-plugin-sdk/packer" - - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2021-11-01/compute" - "github.com/Azure/go-autorest/autorest/to" ) var _ multistep.Step = &StepCreateSnapshotset{} @@ -28,6 +26,13 @@ type StepCreateSnapshotset struct { SkipCleanup bool snapshots Diskset + + create func(context.Context, client.AzureClientSet, snapshots.SnapshotId, snapshots.Snapshot) error +} + +func NewStepCreateSnapshotset(step *StepCreateSnapshotset) *StepCreateSnapshotset { + step.create = step.createSnapshot + return step } func (s *StepCreateSnapshotset) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { @@ -62,36 +67,32 @@ func (s *StepCreateSnapshotset) Run(ctx context.Context, state multistep.StateBa ui.Say(fmt.Sprintf("Creating snapshot %q", ssr)) - snapshot := compute.Snapshot{ - Location: to.StringPtr(s.Location), - SnapshotProperties: &compute.SnapshotProperties{ - CreationData: &compute.CreationData{ - CreateOption: compute.DiskCreateOptionCopy, - SourceResourceID: to.StringPtr(resource.String()), + resourceID := resource.String() + snapshot := snapshots.Snapshot{ + Location: s.Location, + Properties: &snapshots.SnapshotProperties{ + CreationData: snapshots.CreationData{ + CreateOption: snapshots.DiskCreateOptionCopy, + SourceResourceId: &resourceID, }, - Incremental: to.BoolPtr(false), + Incremental: common.BoolPtr(false), }, } - - f, err := azcli.SnapshotsClient().CreateOrUpdate(ctx, ssr.ResourceGroup, ssr.ResourceName.String(), snapshot) + snapshotSDKID := snapshots.NewSnapshotID(azcli.SubscriptionID(), ssr.ResourceGroup, ssr.ResourceName.String()) + err = s.create(ctx, azcli, snapshotSDKID, 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) createSnapshot(ctx context.Context, azcli client.AzureClientSet, id snapshots.SnapshotId, snapshot snapshots.Snapshot) error { + return azcli.SnapshotsClient().CreateOrUpdateThenPoll(ctx, id, snapshot) +} + func (s *StepCreateSnapshotset) Cleanup(state multistep.StateBag) { if !s.SkipCleanup { azcli := state.Get("azureclient").(client.AzureClientSet) @@ -99,13 +100,10 @@ func (s *StepCreateSnapshotset) Cleanup(state multistep.StateBag) { for _, resource := range s.snapshots { + snapshotID := snapshots.NewSnapshotID(azcli.SubscriptionID(), resource.ResourceGroup, resource.ResourceName.String()) 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()) - } + err := azcli.SnapshotsClient().RevokeAccessThenPoll(context.TODO(), snapshotID) if err != nil { log.Printf("StepCreateSnapshotset.Cleanup: error: %+v", err) ui.Error(fmt.Sprintf("error deleting snapshot %q: %v.", resource, err)) @@ -114,11 +112,7 @@ func (s *StepCreateSnapshotset) Cleanup(state multistep.StateBag) { 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()) - } + err := azcli.SnapshotsClient().DeleteThenPoll(context.TODO(), snapshotID) 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 index 203550ec..7638e517 100644 --- a/builder/azure/chroot/step_create_snapshotset_test.go +++ b/builder/azure/chroot/step_create_snapshotset_test.go @@ -5,14 +5,13 @@ package chroot import ( "context" - "io/ioutil" - "net/http" "reflect" - "regexp" + "sort" "testing" - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2021-11-01/compute" - "github.com/Azure/go-autorest/autorest" + "github.com/google/go-cmp/cmp" + "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-02/snapshots" + "github.com/hashicorp/packer-plugin-azure/builder/azure/common" "github.com/hashicorp/packer-plugin-azure/builder/azure/common/client" "github.com/hashicorp/packer-plugin-sdk/multistep" packersdk "github.com/hashicorp/packer-plugin-sdk/packer" @@ -25,12 +24,12 @@ func TestStepCreateSnapshot_Run(t *testing.T) { Location string } tests := []struct { - name string - fields fields - diskset Diskset - want multistep.StepAction - wantSnapshotset Diskset - expectedPutBody string + name string + fields fields + diskset Diskset + want multistep.StepAction + wantSnapshotset Diskset + expectedSnapshots []snapshots.Snapshot }{ { name: "happy path", @@ -39,16 +38,18 @@ func TestStepCreateSnapshot_Run(t *testing.T) { 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" + expectedSnapshots: []snapshots.Snapshot{ + { + Location: "region1", + Properties: &snapshots.SnapshotProperties{ + CreationData: snapshots.CreationData{ + SourceResourceId: common.StringPtr("/subscriptions/12345/resourceGroups/group1/providers/Microsoft.Compute/disks/disk1"), + CreateOption: "Copy", + }, + Incremental: common.BoolPtr(false), }, - "incremental": false - } - }`, + }, + }, wantSnapshotset: diskset("/subscriptions/1234/resourceGroups/rg/providers/Microsoft.Compute/snapshots/osdisk-snap"), }, { @@ -69,6 +70,48 @@ func TestStepCreateSnapshot_Run(t *testing.T) { "/subscriptions/1234/resourceGroups/rg/providers/Microsoft.Compute/snapshots/datadisk-snap1", "/subscriptions/1234/resourceGroups/rg/providers/Microsoft.Compute/snapshots/datadisk-snap2", ), + expectedSnapshots: []snapshots.Snapshot{ + { + Location: "region1", + Properties: &snapshots.SnapshotProperties{ + CreationData: snapshots.CreationData{ + SourceResourceId: common.StringPtr("/subscriptions/12345/resourceGroups/group1/providers/Microsoft.Compute/disks/osdisk"), + CreateOption: "Copy", + }, + Incremental: common.BoolPtr(false), + }, + }, + { + Location: "region1", + Properties: &snapshots.SnapshotProperties{ + CreationData: snapshots.CreationData{ + SourceResourceId: common.StringPtr("/subscriptions/12345/resourceGroups/group1/providers/Microsoft.Compute/disks/datadisk1"), + CreateOption: "Copy", + }, + Incremental: common.BoolPtr(false), + }, + }, + { + Location: "region1", + Properties: &snapshots.SnapshotProperties{ + CreationData: snapshots.CreationData{ + SourceResourceId: common.StringPtr("/subscriptions/12345/resourceGroups/group1/providers/Microsoft.Compute/disks/datadisk2"), + CreateOption: "Copy", + }, + Incremental: common.BoolPtr(false), + }, + }, + { + Location: "region1", + Properties: &snapshots.SnapshotProperties{ + CreationData: snapshots.CreationData{ + SourceResourceId: common.StringPtr("/subscriptions/12345/resourceGroups/group1/providers/Microsoft.Compute/disks/datadisk3"), + CreateOption: "Copy", + }, + Incremental: common.BoolPtr(false), + }, + }, + }, }, { name: "invalid ResourceID", @@ -81,37 +124,21 @@ func TestStepCreateSnapshot_Run(t *testing.T) { }, } 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("azureclient", &client.AzureClientSetMock{}) state.Put("ui", packersdk.TestUi(t)) state.Put(stateBagKey_Diskset, tt.diskset) t.Run(tt.name, func(t *testing.T) { + actualSnapshots := []snapshots.Snapshot{} s := &StepCreateSnapshotset{ OSDiskSnapshotID: tt.fields.OSDiskSnapshotID, DataDiskSnapshotIDPrefix: tt.fields.DataDiskSnapshotIDPrefix, Location: tt.fields.Location, + create: func(ctx context.Context, azcli client.AzureClientSet, id snapshots.SnapshotId, snapshot snapshots.Snapshot) error { + actualSnapshots = append(actualSnapshots, snapshot) + return nil + }, } if got := s.Run(context.TODO(), state); !reflect.DeepEqual(got, tt.want) { t.Errorf("StepCreateSnapshot.Run() = %v, want %v", got, tt.want) @@ -124,21 +151,24 @@ func TestStepCreateSnapshot_Run(t *testing.T) { } } + if len(tt.expectedSnapshots) > 0 { + sort.Slice(tt.expectedSnapshots, func(i, j int) bool { + return *tt.expectedSnapshots[i].Properties.CreationData.SourceResourceId < *tt.expectedSnapshots[j].Properties.CreationData.SourceResourceId + }) + sort.Slice(actualSnapshots, func(i, j int) bool { + return *actualSnapshots[i].Properties.CreationData.SourceResourceId < *actualSnapshots[j].Properties.CreationData.SourceResourceId + }) + if diff := cmp.Diff(tt.expectedSnapshots, actualSnapshots); diff != "" { + t.Fatal(diff) + } + } }) } } 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("azureclient", &client.AzureClientSetMock{}) state.Put("ui", packersdk.TestUi(t)) s := &StepCreateSnapshotset{ @@ -146,88 +176,3 @@ func TestStepCreateSnapshot_Cleanup_skipped(t *testing.T) { } 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_get_source_image_name.go b/builder/azure/chroot/step_get_source_image_name.go index cce24400..b4278c6a 100644 --- a/builder/azure/chroot/step_get_source_image_name.go +++ b/builder/azure/chroot/step_get_source_image_name.go @@ -8,6 +8,7 @@ import ( "fmt" "log" + "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-03/galleryimageversions" "github.com/hashicorp/packer-plugin-azure/builder/azure/common/client" "github.com/hashicorp/packer-plugin-sdk/multistep" packersdk "github.com/hashicorp/packer-plugin-sdk/packer" @@ -25,6 +26,13 @@ type StepGetSourceImageName struct { Location string GeneratedData *packerbuilderdata.GeneratedData + + get func(context.Context, client.AzureClientSet, galleryimageversions.ImageVersionId) (*galleryimageversions.GalleryImageVersion, error) +} + +func NewStepGetSourceImageName(step *StepGetSourceImageName) *StepGetSourceImageName { + step.get = step.getSharedImageGalleryVersion + return step } func (s *StepGetSourceImageName) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { @@ -46,17 +54,16 @@ func (s *StepGetSourceImageName) Run(ctx context.Context, state multistep.StateB return multistep.ActionContinue } - client := azcli.GalleryImageVersionsClient() - image, err := client.Get(ctx, imageID.ResourceGroup, imageID.ResourceName[0], imageID.ResourceName[1], imageID.ResourceName[2], "") + imageVersionID := galleryimageversions.NewImageVersionID(azcli.SubscriptionID(), imageID.ResourceGroup, imageID.ResourceName[0], imageID.ResourceName[1], imageID.ResourceName[2]) + image, err := s.get(ctx, azcli, imageVersionID) if err != nil { log.Printf("[TRACE] error retrieving managed image name for shared source image %q: %v", s.SourceImageResourceID, err) s.GeneratedData.Put("SourceImageName", "ERR_SOURCE_IMAGE_NAME_NOT_FOUND") return multistep.ActionContinue } - - if image.GalleryImageVersionProperties != nil && image.GalleryImageVersionProperties.StorageProfile != nil && - image.GalleryImageVersionProperties.StorageProfile.Source != nil && image.GalleryImageVersionProperties.StorageProfile.Source.ID != nil { - id := *image.GalleryImageVersionProperties.StorageProfile.Source.ID + if image.Properties != nil && + image.Properties.StorageProfile.Source != nil && image.Properties.StorageProfile.Source.Id != nil { + id := *image.Properties.StorageProfile.Source.Id ui.Say(fmt.Sprintf(" -> SourceImageName: '%s'", id)) s.GeneratedData.Put("SourceImageName", id) return multistep.ActionContinue @@ -76,5 +83,17 @@ func (s *StepGetSourceImageName) Run(ctx context.Context, state multistep.StateB return multistep.ActionContinue } +func (s *StepGetSourceImageName) getSharedImageGalleryVersion(ctx context.Context, azclient client.AzureClientSet, id galleryimageversions.ImageVersionId) (*galleryimageversions.GalleryImageVersion, error) { + + imageVersionResult, err := azclient.GalleryImageVersionsClient().Get(ctx, id, galleryimageversions.DefaultGetOperationOptions()) + if err != nil { + return nil, err + } + if imageVersionResult.Model == nil { + return nil, fmt.Errorf("SDK returned empty model") + } + return imageVersionResult.Model, nil +} + func (*StepGetSourceImageName) Cleanup(multistep.StateBag) { } diff --git a/builder/azure/chroot/step_get_source_image_name_test.go b/builder/azure/chroot/step_get_source_image_name_test.go index eab2655c..f36f4a23 100644 --- a/builder/azure/chroot/step_get_source_image_name_test.go +++ b/builder/azure/chroot/step_get_source_image_name_test.go @@ -6,13 +6,11 @@ package chroot import ( "bytes" "context" - "io/ioutil" - "net/http" - "strings" + "fmt" "testing" - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2021-11-01/compute" - "github.com/Azure/go-autorest/autorest" + "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-03/galleryimageversions" + "github.com/hashicorp/packer-plugin-azure/builder/azure/common" "github.com/hashicorp/packer-plugin-azure/builder/azure/common/client" "github.com/hashicorp/packer-plugin-sdk/multistep" packersdk "github.com/hashicorp/packer-plugin-sdk/packer" @@ -86,36 +84,63 @@ func TestChrootStepGetSourceImageName_SharedImage(t *testing.T) { genData := packerbuilderdata.GeneratedData{State: state} tc := []struct { - name string - step *StepGetSourceImageName - expected string + name string + step *StepGetSourceImageName + expected string + mockedGalleryReturn *galleryimageversions.GalleryImageVersion + expectedImageId galleryimageversions.ImageVersionId + SourceImageResourceID string + GeneratedData packerbuilderdata.GeneratedData }{ { - name: "SharedImageWithMangedImageSource", - step: &StepGetSourceImageName{ - SourceImageResourceID: "/subscriptions/1234/resourceGroups/bar/providers/Microsoft.Compute/galleries/test/images/foo/versions/1.0.6", - GeneratedData: &genData, + name: "SharedImageWithMangedImageSource", + SourceImageResourceID: "/subscriptions/1234/resourceGroups/bar/providers/Microsoft.Compute/galleries/test/images/foo/versions/1.0.6", + GeneratedData: genData, + mockedGalleryReturn: &galleryimageversions.GalleryImageVersion{ + Properties: &galleryimageversions.GalleryImageVersionProperties{ + StorageProfile: galleryimageversions.GalleryImageVersionStorageProfile{ + Source: &galleryimageversions.GalleryArtifactVersionFullSource{ + Id: common.StringPtr("/subscription/resource/managed/image/name/as/source"), + }, + }, + }, + }, + expectedImageId: galleryimageversions.ImageVersionId{ + SubscriptionId: "1234", + ResourceGroupName: "bar", + GalleryName: "test", + ImageName: "foo", + VersionName: "1.0.6", }, expected: "/subscription/resource/managed/image/name/as/source", }, { - name: "SimulatedBadImageResponse", - step: &StepGetSourceImageName{ - SourceImageResourceID: "/subscriptions/1234/resourceGroups/bar/providers/Microsoft.Compute/galleries/test/images/foo/versions/0.0.0", - GeneratedData: &genData, - }, - expected: "ERR_SOURCE_IMAGE_NAME_NOT_FOUND", + name: "SimulatedBadImageResponse", + SourceImageResourceID: "/subscriptions/1234/resourceGroups/bar/providers/Microsoft.Compute/galleries/test/images/foo/versions/0.0.0", + GeneratedData: genData, + expected: "ERR_SOURCE_IMAGE_NAME_NOT_FOUND", }, } for _, tt := range tc { tt := tt t.Run(tt.name, func(t *testing.T) { + var actualID galleryimageversions.ImageVersionId + step := StepGetSourceImageName{ + SourceImageResourceID: tt.SourceImageResourceID, + GeneratedData: &tt.GeneratedData, + get: func(ctx context.Context, azcli client.AzureClientSet, id galleryimageversions.ImageVersionId) (*galleryimageversions.GalleryImageVersion, error) { + actualID = id + if tt.mockedGalleryReturn == nil { + return nil, fmt.Errorf("Generic error") + } + return tt.mockedGalleryReturn, nil + }, + } state.Put("azureclient", &client.AzureClientSetMock{ - SubscriptionIDMock: "1234", - GalleryImageVersionsClientMock: MockGalleryImageClient("1234"), + SubscriptionIDMock: "1234", }) - tt.step.Run(context.TODO(), state) + step.Run(context.TODO(), state) got := state.Get("generated_data").(map[string]interface{}) v, ok := got["SourceImageName"] if !ok { @@ -125,35 +150,12 @@ func TestChrootStepGetSourceImageName_SharedImage(t *testing.T) { if v != tt.expected { t.Errorf("expected SourceImageName to be set to %q but got %q", tt.expected, v) } + + if tt.mockedGalleryReturn != nil { + if actualID != tt.expectedImageId { + t.Errorf("expected %s but got %s Gallery Image Version ID passed into client", tt.expectedImageId, actualID) + } + } }) } - -} - -func MockGalleryImageClient(subID string) compute.GalleryImageVersionsClient { - giv := compute.NewGalleryImageVersionsClient(subID) - giv.Sender = autorest.SenderFunc(func(r *http.Request) (*http.Response, error) { - - if strings.HasSuffix(r.URL.Path, "versions/0.0.0") { - return &http.Response{ - Request: r, - Body: http.NoBody, - StatusCode: 200, - }, nil - } - - return &http.Response{ - Request: r, - Body: ioutil.NopCloser(strings.NewReader(`{ - "properties": { "storageProfile": { - "Source": { - "ID": "/subscription/resource/managed/image/name/as/source" - } - } } - }`)), - StatusCode: 200, - }, nil - }) - - return giv } diff --git a/builder/azure/chroot/step_mount_device_test.go b/builder/azure/chroot/step_mount_device_test.go index d9fd3a98..96ec1b10 100644 --- a/builder/azure/chroot/step_mount_device_test.go +++ b/builder/azure/chroot/step_mount_device_test.go @@ -34,8 +34,7 @@ func TestStepMountDevice_Run(t *testing.T) { } var gotCommand string - var wrapper common.CommandWrapper - wrapper = func(ran string) (string, error) { + var wrapper common.CommandWrapper = func(ran string) (string, error) { gotCommand = ran return "", nil } diff --git a/builder/azure/chroot/step_resolve_plaform_image_version.go b/builder/azure/chroot/step_resolve_plaform_image_version.go index 224173a0..273d25f9 100644 --- a/builder/azure/chroot/step_resolve_plaform_image_version.go +++ b/builder/azure/chroot/step_resolve_plaform_image_version.go @@ -9,7 +9,7 @@ import ( "log" "strings" - "github.com/Azure/go-autorest/autorest/to" + "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-01/virtualmachineimages" "github.com/hashicorp/packer-plugin-azure/builder/azure/common/client" "github.com/hashicorp/packer-plugin-sdk/multistep" packersdk "github.com/hashicorp/packer-plugin-sdk/packer" @@ -18,7 +18,14 @@ import ( // StepResolvePlatformImageVersion resolves the exact PIR version when the version is 'latest' type StepResolvePlatformImageVersion struct { *client.PlatformImage - Location string + ResourceGroupName string + Location string + list func(context.Context, client.AzureClientSet, virtualmachineimages.SkuId, virtualmachineimages.ListOperationOptions) (*[]virtualmachineimages.VirtualMachineImageResource, error) +} + +func NewStepResolvePlatformImageVersion(step *StepResolvePlatformImageVersion) *StepResolvePlatformImageVersion { + step.list = step.listVMImages + return step } // Run retrieves all available versions of a PIR image and stores the latest in the PlatformImage @@ -28,7 +35,17 @@ func (pi *StepResolvePlatformImageVersion) Run(ctx context.Context, state multis 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) + //vmi, err := azcli.VirtualMachineImagesClient().GetLatest(ctx, pi.Publisher, pi.Offer, pi.Sku, pi.Location) + vmMachineImagesSKUID := virtualmachineimages.NewSkuID(azcli.SubscriptionID(), pi.Location, pi.Publisher, pi.Offer, pi.Sku) + orderBy := "name desc" + vmList, err := pi.list( + ctx, + azcli, + vmMachineImagesSKUID, + virtualmachineimages.ListOperationOptions{ + Orderby: &orderBy, + }, + ) if err != nil { log.Printf("StepResolvePlatformImageVersion.Run: error: %+v", err) err := fmt.Errorf("error retieving latest version of %q: %v", pi.URN(), err) @@ -36,7 +53,15 @@ func (pi *StepResolvePlatformImageVersion) Run(ctx context.Context, state multis ui.Error(err.Error()) return multistep.ActionHalt } - pi.Version = to.String(vmi.Name) + + if len(*vmList) == 0 { + err := fmt.Errorf("%s:%s:%s:latest could not be found in location %s", pi.Publisher, pi.Offer, pi.Sku, pi.Location) + state.Put("error", err) + ui.Error(err.Error()) + return multistep.ActionHalt + } + + pi.Version = (*vmList)[0].Name ui.Say("Resolved latest version of source image: " + pi.Version) } else { ui.Say("Nothing to do, version is not 'latest'") @@ -44,5 +69,19 @@ func (pi *StepResolvePlatformImageVersion) Run(ctx context.Context, state multis return multistep.ActionContinue } +func (s *StepResolvePlatformImageVersion) listVMImages(ctx context.Context, azcli client.AzureClientSet, skuID virtualmachineimages.SkuId, operations virtualmachineimages.ListOperationOptions) (*[]virtualmachineimages.VirtualMachineImageResource, error) { + result, err := azcli.VirtualMachineImagesClient().List( + ctx, + skuID, + operations, + ) + if err != nil { + return nil, err + } + if result.Model == nil { + return nil, fmt.Errorf("SDK returned empty model") + } + return result.Model, nil +} 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 index cbee35c6..37eb881f 100644 --- a/builder/azure/chroot/step_resolve_plaform_image_version_test.go +++ b/builder/azure/chroot/step_resolve_plaform_image_version_test.go @@ -5,48 +5,55 @@ package chroot import ( "context" - "io/ioutil" - "net/http" - "strings" "testing" "time" "github.com/hashicorp/packer-plugin-azure/builder/azure/common/client" "github.com/hashicorp/packer-plugin-sdk/multistep" - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2021-11-01/compute" - "github.com/Azure/go-autorest/autorest" + "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-01/virtualmachineimages" ) func TestStepResolvePlatformImageVersion_Run(t *testing.T) { + var expectedSkuId, actualSkuId virtualmachineimages.SkuId + expectedSku := "Linux" + expectedOffer := "Offer" + expectedPublisher := "Arch" + subscriptionID := "1234" + expectedLocation := "linuxland" + expectedSkuId = virtualmachineimages.NewSkuID(subscriptionID, expectedLocation, expectedPublisher, expectedOffer, expectedSku) + var actualListOperations virtualmachineimages.ListOperationOptions + returnedVMImages := []virtualmachineimages.VirtualMachineImageResource{ + { + Name: "1.2.3", + }, + { + Name: "0.2.1", + }, + } pi := &StepResolvePlatformImageVersion{ PlatformImage: &client.PlatformImage{ - Version: "latest", - }} + Version: "latest", + Sku: expectedSku, + Offer: expectedOffer, + Publisher: expectedPublisher, + }, + Location: expectedLocation, + list: func(ctx context.Context, azcli client.AzureClientSet, skuID virtualmachineimages.SkuId, operations virtualmachineimages.ListOperationOptions) (*[]virtualmachineimages.VirtualMachineImageResource, error) { - 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 - }) + actualSkuId = skuID + actualListOperations = operations + return &returnedVMImages, nil + }, + } state := new(multistep.BasicStateBag) - state.Put("azureclient", &client.AzureClientSetMock{ - VirtualMachineImagesClientMock: client.VirtualMachineImagesClient{ - VirtualMachineImagesClientAPI: m}}) - ui, getErrs := testUI() + ui, _ := testUI() + state.Put("azureclient", &client.AzureClientSetMock{ + SubscriptionIDMock: subscriptionID, + }) state.Put("ui", ui) ctx, cancel := context.WithTimeout(context.Background(), time.Second) @@ -60,6 +67,10 @@ func TestStepResolvePlatformImageVersion_Run(t *testing.T) { if pi.PlatformImage.Version != "1.2.3" { t.Errorf("Expected version '1.2.3', but got %q", pi.PlatformImage.Version) } - - _ = getErrs + if actualSkuId != expectedSkuId { + t.Fatalf("Expected sku ID %+v got sku ID %+v", expectedSkuId, actualSkuId) + } + if *actualListOperations.Orderby != "name desc" { + t.Fatalf("Expected name desc order by list operation, got %s", *actualListOperations.Orderby) + } } diff --git a/builder/azure/chroot/step_verify_shared_image_destination.go b/builder/azure/chroot/step_verify_shared_image_destination.go index 5f00b367..f1650701 100644 --- a/builder/azure/chroot/step_verify_shared_image_destination.go +++ b/builder/azure/chroot/step_verify_shared_image_destination.go @@ -9,8 +9,8 @@ import ( "log" "strings" - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2021-11-01/compute" - "github.com/Azure/go-autorest/autorest/to" + "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-03/galleryimages" + "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-03/galleryimageversions" "github.com/hashicorp/packer-plugin-azure/builder/azure/common/client" "github.com/hashicorp/packer-plugin-sdk/multistep" packersdk "github.com/hashicorp/packer-plugin-sdk/packer" @@ -21,8 +21,16 @@ 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 + Image SharedImageGalleryDestination + Location string + listVersions func(context.Context, client.AzureClientSet, galleryimageversions.GalleryImageId) ([]galleryimageversions.GalleryImageVersion, error) + getImage func(context.Context, client.AzureClientSet, galleryimages.GalleryImageId) (*galleryimages.GalleryImage, error) +} + +func NewStepVerifySharedImageDestination(step *StepVerifySharedImageDestination) *StepVerifySharedImageDestination { + step.getImage = step.getGalleryImage + step.listVersions = step.listGalleryVersions + return step } // Run retrieves the image metadata from Azure and compares the location to Location. Verifies the OS Type. @@ -45,78 +53,99 @@ func (s *StepVerifySharedImageDestination) Run(ctx context.Context, state multis s.Image.ImageName, ) - ui.Say(fmt.Sprintf("Validating that shared image %s exists", - imageURI)) - - image, err := azcli.GalleryImagesClient().Get(ctx, + ui.Say(fmt.Sprintf("Validating that shared image %s exists", imageURI)) + galleryImageID := galleryimages.NewGalleryImageID( + azcli.SubscriptionID(), s.Image.ResourceGroup, s.Image.GalleryName, - s.Image.ImageName) + s.Image.ImageName, + ) + image, err := s.getImage(ctx, azcli, galleryImageID) if err != nil { return errorMessage("Error retrieving shared image %q: %+v ", imageURI, err) } - if image.ID == nil || *image.ID == "" { + 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)) + if image.Properties == nil { + return errorMessage("Could not retrieve shared image properties for image %q.", image.Id) } - location := to.String(image.Location) + location := image.Location - log.Printf("StepVerifySharedImageDestination:Run: Image %q, Location: %q, HvGen: %q, osState: %q", - to.String(image.ID), + log.Printf("StepVerifySharedImageDestination:Run: Image %+v, Location: %+v, HvGen: %+v, osState: %+v", + *(image.Id), location, - image.GalleryImageProperties.HyperVGeneration, - image.GalleryImageProperties.OsState) + image.Properties.HyperVGeneration, + image.Properties.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), + return errorMessage("Destination shared image resource %q is in a different location (%q) than this VM (%q).", + *image.Id, location, s.Location) } - if image.GalleryImageProperties.OsType != compute.OperatingSystemTypesLinux { + if image.Properties.OsType != galleryimages.OperatingSystemTypesLinux { 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) + *(image.Id), + image.Properties.OsType) } ui.Say(fmt.Sprintf("Found image %s in location %s", - to.String(image.ID), - to.String(image.Location))) + *image.Id, + image.Location, + )) - versions, err := azcli.GalleryImageVersionsClient().ListByGalleryImageComplete(ctx, + // TODO Suggest moving gallery image ID to common IDs library + // so we don't have to define two different versions of the same resource ID + galleryImageIDForList := galleryimageversions.NewGalleryImageID( + azcli.SubscriptionID(), s.Image.ResourceGroup, s.Image.GalleryName, - s.Image.ImageName) + s.Image.ImageName, + ) + versions, err := s.listVersions(ctx, azcli, + galleryImageIDForList) 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() - + for _, version := range versions { if version.Name == nil { - return errorMessage("Could not retrieve versions for image %q: unexpected nil name", to.String(image.ID)) + return errorMessage("Could not retrieve versions for image %q: unexpected nil name", 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 errorMessage("Shared image version %q already exists for image %q.", s.Image.ImageVersion, *image.Id) } } return multistep.ActionContinue } +func (s *StepVerifySharedImageDestination) getGalleryImage(ctx context.Context, azcli client.AzureClientSet, id galleryimages.GalleryImageId) (*galleryimages.GalleryImage, error) { + res, err := azcli.GalleryImagesClient().Get(ctx, id) + if err != nil { + return nil, err + } + if res.Model == nil { + return nil, fmt.Errorf("SDK returned empty model") + } + return res.Model, nil +} + +func (s *StepVerifySharedImageDestination) listGalleryVersions(ctx context.Context, azcli client.AzureClientSet, id galleryimageversions.GalleryImageId) ([]galleryimageversions.GalleryImageVersion, error) { + res, err := azcli.GalleryImageVersionsClient().ListByGalleryImageComplete(ctx, id) + if err != nil { + return nil, err + } + if res.Items == nil { + return nil, fmt.Errorf("SDK returned empty model") + } + return res.Items, nil +} 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 index de33d1c3..e84e1e13 100644 --- a/builder/azure/chroot/step_verify_shared_image_destination_test.go +++ b/builder/azure/chroot/step_verify_shared_image_destination_test.go @@ -5,18 +5,16 @@ package chroot import ( "context" - "io/ioutil" - "net/http" + "fmt" "reflect" - "strings" "testing" + "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-03/galleryimages" + "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-03/galleryimageversions" + "github.com/hashicorp/packer-plugin-azure/builder/azure/common" "github.com/hashicorp/packer-plugin-azure/builder/azure/common/client" "github.com/hashicorp/packer-plugin-sdk/multistep" packersdk "github.com/hashicorp/packer-plugin-sdk/packer" - - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2021-11-01/compute" - "github.com/Azure/go-autorest/autorest" ) func TestStepVerifySharedImageDestination_Run(t *testing.T) { @@ -47,7 +45,7 @@ func TestStepVerifySharedImageDestination_Run(t *testing.T) { { 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" `, + wantErr: "Error retrieving shared image \"/subscriptions/subscriptionID/resourcegroup/other-rg/providers/Microsoft.Compute/galleries/gallery/images/image\": Not Found ", fields: fields{ Image: SharedImageGalleryDestination{ ResourceGroup: "other-rg", @@ -61,7 +59,7 @@ func TestStepVerifySharedImageDestination_Run(t *testing.T) { { 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.", + wantErr: "Destination shared image resource \"image-resourceid-goes-here\" is in a different location (\"region1\") than this VM (\"other-region\").", fields: fields{ Image: SharedImageGalleryDestination{ ResourceGroup: "rg", @@ -102,70 +100,9 @@ func TestStepVerifySharedImageDestination_Run(t *testing.T) { }, } 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, + SubscriptionIDMock: "subscriptionID", }) state.Put("ui", packersdk.TestUi(t)) @@ -173,17 +110,47 @@ func TestStepVerifySharedImageDestination_Run(t *testing.T) { s := &StepVerifySharedImageDestination{ Image: tt.fields.Image, Location: tt.fields.Location, + getImage: func(ctx context.Context, acs client.AzureClientSet, id galleryimages.GalleryImageId) (*galleryimages.GalleryImage, error) { + switch { + case id.ImageName == "image" && id.GalleryName == "gallery" && id.ResourceGroupName == "rg": + return &galleryimages.GalleryImage{ + Id: common.StringPtr("image-resourceid-goes-here"), + Location: "region1", + Properties: &galleryimages.GalleryImageProperties{ + OsType: galleryimages.OperatingSystemTypesLinux, + }, + }, nil + case id.ImageName == "windowsimage" && id.GalleryName == "gallery" && id.ResourceGroupName == "rg": + return &galleryimages.GalleryImage{ + Id: common.StringPtr("windows-image-resourceid-goes-here"), + Location: "region1", + Properties: &galleryimages.GalleryImageProperties{ + OsType: galleryimages.OperatingSystemTypesWindows, + }, + }, nil + } + return nil, fmt.Errorf("Not Found") + }, + listVersions: func(ctx context.Context, acs client.AzureClientSet, id galleryimageversions.GalleryImageId) ([]galleryimageversions.GalleryImageVersion, error) { + return []galleryimageversions.GalleryImageVersion{ + { + Name: common.StringPtr("2.3.4"), + }, + }, nil + }, } + 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) + + 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") } - } 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 index 7327024a..51d494be 100644 --- a/builder/azure/chroot/step_verify_shared_image_source.go +++ b/builder/azure/chroot/step_verify_shared_image_source.go @@ -9,8 +9,8 @@ import ( "log" "strings" - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2021-11-01/compute" - "github.com/Azure/go-autorest/autorest/to" + "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-03/galleryimages" + "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-03/galleryimageversions" "github.com/hashicorp/packer-plugin-azure/builder/azure/common/client" "github.com/hashicorp/packer-plugin-sdk/multistep" packersdk "github.com/hashicorp/packer-plugin-sdk/packer" @@ -24,6 +24,15 @@ type StepVerifySharedImageSource struct { SharedImageID string SubscriptionID string Location string + + getVersion func(context.Context, client.AzureClientSet, galleryimageversions.ImageVersionId) (*galleryimageversions.GalleryImageVersion, error) + getImage func(context.Context, client.AzureClientSet, galleryimages.GalleryImageId) (*galleryimages.GalleryImage, error) +} + +func NewStepVerifySharedImageSource(step *StepVerifySharedImageSource) *StepVerifySharedImageSource { + step.getImage = step.getGalleryImage + step.getVersion = step.getGalleryVersion + return step } // Run retrieves the image metadata from Azure and compares the location to Location. Verifies the OS Type. @@ -52,35 +61,36 @@ func (s *StepVerifySharedImageSource) Run(ctx context.Context, state multistep.S ui.Say(fmt.Sprintf("Validating that shared image version %q exists", s.SharedImageID)) - version, err := azcli.GalleryImageVersionsClient().Get(ctx, + galleryImageVersionID := galleryimageversions.NewImageVersionID( + azcli.SubscriptionID(), resource.ResourceGroup, resource.ResourceName[0], resource.ResourceName[1], resource.ResourceName[2], - "") + ) + version, err := s.getVersion(ctx, azcli, galleryImageVersionID) if err != nil { return errorMessage("Error retrieving shared image version %q: %+v ", s.SharedImageID, err) } - if version.ID == nil || *version.ID == "" { + 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 { + if version.Properties == nil || + version.Properties.PublishingProfile == nil || + version.Properties.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)) + targetLocations := make([]string, 0, len(*version.Properties.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) { + for _, tr := range *version.Properties.PublishingProfile.TargetRegions { + location := client.NormalizeLocation(tr.Name) + targetLocations = append(targetLocations, location) + if strings.EqualFold(vmLocation, location) { locationFound = true break } @@ -91,32 +101,34 @@ func (s *StepVerifySharedImageSource) Run(ctx context.Context, state multistep.S } imageResource, _ := resource.Parent() - image, err := azcli.GalleryImagesClient().Get(ctx, + galleryImageID := galleryimages.NewGalleryImageID( + azcli.SubscriptionID(), resource.ResourceGroup, resource.ResourceName[0], - resource.ResourceName[1]) - + resource.ResourceName[1], + ) + image, err := s.getImage(ctx, azcli, galleryImageID) if err != nil { return errorMessage("Error retrieving shared image %q: %+v ", imageResource.String(), err) } - if image.ID == nil || *image.ID == "" { + 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 { + if image.Properties == 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) + log.Printf("StepVerifySharedImageSource:Run: Image %+v, HvGen: %+v, osState: %+v", + &image.Id, + image.Properties.HyperVGeneration, + image.Properties.OsState) - if image.GalleryImageProperties.OsType != compute.OperatingSystemTypesLinux { + if image.Properties.OsType != galleryimages.OperatingSystemTypesLinux { 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) + &image.Id, + image.Properties.OsType) } ui.Say(fmt.Sprintf("Found image source image version %q, available in location %s", @@ -126,4 +138,25 @@ func (s *StepVerifySharedImageSource) Run(ctx context.Context, state multistep.S return multistep.ActionContinue } +func (s *StepVerifySharedImageSource) getGalleryVersion(ctx context.Context, azcli client.AzureClientSet, id galleryimageversions.ImageVersionId) (*galleryimageversions.GalleryImageVersion, error) { + res, err := azcli.GalleryImageVersionsClient().Get(ctx, id, galleryimageversions.DefaultGetOperationOptions()) + if err != nil { + return nil, err + } + if res.Model == nil { + return nil, fmt.Errorf("SDK returned empty model") + } + return res.Model, nil +} + +func (s *StepVerifySharedImageSource) getGalleryImage(ctx context.Context, azcli client.AzureClientSet, id galleryimages.GalleryImageId) (*galleryimages.GalleryImage, error) { + res, err := azcli.GalleryImagesClient().Get(ctx, id) + if err != nil { + return nil, err + } + if res.Model == nil { + return nil, fmt.Errorf("SDK returned empty model") + } + return res.Model, nil +} 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 index a8da27e7..05075dc4 100644 --- a/builder/azure/chroot/step_verify_shared_image_source_test.go +++ b/builder/azure/chroot/step_verify_shared_image_source_test.go @@ -5,15 +5,14 @@ package chroot import ( "context" - "io/ioutil" - "net/http" + "fmt" "reflect" - "regexp" "strings" "testing" - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2021-11-01/compute" - "github.com/Azure/go-autorest/autorest" + "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-03/galleryimages" + "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-03/galleryimageversions" + "github.com/hashicorp/packer-plugin-azure/builder/azure/common" "github.com/hashicorp/packer-plugin-azure/builder/azure/common/client" "github.com/hashicorp/packer-plugin-sdk/multistep" packersdk "github.com/hashicorp/packer-plugin-sdk/packer" @@ -26,10 +25,12 @@ func TestStepVerifySharedImageSource_Run(t *testing.T) { Location string } tests := []struct { - name string - fields fields - want multistep.StepAction - wantErr string + name string + fields fields + want multistep.StepAction + wantErr string + shouldCallGetVersion bool + shouldCallGetImage bool }{ { name: "happy path", @@ -37,6 +38,8 @@ func TestStepVerifySharedImageSource_Run(t *testing.T) { SharedImageID: "/subscriptions/subscriptionID/resourceGroups/rg/providers/Microsoft.Compute/galleries/myGallery/images/myImage/versions/1.2.3", Location: "VM location", }, + shouldCallGetVersion: true, + shouldCallGetImage: true, }, { name: "resource is not a shared image", @@ -61,8 +64,9 @@ func TestStepVerifySharedImageSource_Run(t *testing.T) { 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", + want: multistep.ActionHalt, + wantErr: "does not include VM location", + shouldCallGetVersion: true, }, { name: "image not found", @@ -70,8 +74,9 @@ func TestStepVerifySharedImageSource_Run(t *testing.T) { 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", + want: multistep.ActionHalt, + wantErr: "Error retrieving shared image version", + shouldCallGetVersion: true, }, { name: "windows image", @@ -79,92 +84,17 @@ func TestStepVerifySharedImageSource_Run(t *testing.T) { 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", + want: multistep.ActionHalt, + wantErr: "not a Linux image", + shouldCallGetVersion: true, + shouldCallGetImage: true, }, } 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, + SubscriptionIDMock: "subscriptionID", }) state.Put("ui", packersdk.TestUi(t)) @@ -173,6 +103,51 @@ func TestStepVerifySharedImageSource_Run(t *testing.T) { SharedImageID: tt.fields.SharedImageID, SubscriptionID: tt.fields.SubscriptionID, Location: tt.fields.Location, + getImage: func(ctx context.Context, acs client.AzureClientSet, id galleryimages.GalleryImageId) (*galleryimages.GalleryImage, error) { + if !tt.shouldCallGetImage { + t.Fatalf("Expected test to not call getImage but it did") + } + switch { + case strings.HasSuffix(id.ImageName, "windowsImage"): + return &galleryimages.GalleryImage{ + Id: common.StringPtr("image-id"), + Properties: &galleryimages.GalleryImageProperties{ + OsType: galleryimages.OperatingSystemTypesWindows, + }, + }, nil + case strings.HasSuffix(id.ImageName, "myImage"): + return &galleryimages.GalleryImage{ + Id: common.StringPtr("image-id"), + Properties: &galleryimages.GalleryImageProperties{ + OsType: galleryimages.OperatingSystemTypesLinux, + }, + }, nil + default: + return nil, fmt.Errorf("Unexpected image") + } + }, + getVersion: func(ctx context.Context, azcli client.AzureClientSet, id galleryimageversions.ImageVersionId) (*galleryimageversions.GalleryImageVersion, error) { + if !tt.shouldCallGetVersion { + t.Fatalf("Expected test to not call getVersion but it did") + } + switch { + case id.VersionName == "1.2.3": + return &galleryimageversions.GalleryImageVersion{ + Id: common.StringPtr("image-version-id"), + Properties: &galleryimageversions.GalleryImageVersionProperties{ + PublishingProfile: &galleryimageversions.GalleryArtifactPublishingProfileBase{ + TargetRegions: &[]galleryimageversions.TargetRegion{ + { + Name: "vm Location", + }, + }, + }, + }, + }, nil + default: + return nil, fmt.Errorf("Not found") + } + }, } if got := s.Run(context.TODO(), state); !reflect.DeepEqual(got, tt.want) { t.Errorf("StepVerifySharedImageSource.Run() = %v, want %v", got, tt.want) diff --git a/builder/azure/chroot/step_verify_source_disk.go b/builder/azure/chroot/step_verify_source_disk.go index 39f87ff6..ec869efd 100644 --- a/builder/azure/chroot/step_verify_source_disk.go +++ b/builder/azure/chroot/step_verify_source_disk.go @@ -9,9 +9,7 @@ import ( "log" "strings" - "github.com/Azure/go-autorest/autorest/azure" - "github.com/Azure/go-autorest/autorest/to" - + "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-02/disks" "github.com/hashicorp/packer-plugin-azure/builder/azure/common/client" "github.com/hashicorp/packer-plugin-sdk/multistep" packersdk "github.com/hashicorp/packer-plugin-sdk/packer" @@ -20,6 +18,13 @@ import ( type StepVerifySourceDisk struct { SourceDiskResourceID string Location string + + get func(context.Context, client.AzureClientSet, disks.DiskId) (*disks.Disk, error) +} + +func NewStepVerifySourceDisk(step *StepVerifySourceDisk) *StepVerifySourceDisk { + step.get = step.getDisk + return step } func (s StepVerifySourceDisk) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { @@ -27,7 +32,7 @@ func (s StepVerifySourceDisk) Run(ctx context.Context, state multistep.StateBag) ui := state.Get("ui").(packersdk.Ui) ui.Say("Checking source disk location") - resource, err := azure.ParseResourceID(s.SourceDiskResourceID) + resource, err := client.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) @@ -36,7 +41,7 @@ func (s StepVerifySourceDisk) Run(ctx context.Context, state multistep.StateBag) return multistep.ActionHalt } - if !strings.EqualFold(resource.SubscriptionID, azcli.SubscriptionID()) { + if !strings.EqualFold(resource.Subscription, 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()) @@ -46,7 +51,7 @@ func (s StepVerifySourceDisk) Run(ctx context.Context, state multistep.StateBag) return multistep.ActionHalt } - if !(strings.EqualFold(resource.Provider, "Microsoft.Compute") && strings.EqualFold(resource.ResourceType, "disks")) { + if !(strings.EqualFold(resource.Provider, "Microsoft.Compute") && strings.EqualFold(resource.ResourceType.String(), "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) @@ -54,8 +59,8 @@ func (s StepVerifySourceDisk) Run(ctx context.Context, state multistep.StateBag) return multistep.ActionHalt } - disk, err := azcli.DisksClient().Get(ctx, - resource.ResourceGroup, resource.ResourceName) + diskId := disks.NewDiskID(azcli.SubscriptionID(), resource.ResourceGroup, resource.ResourceName.String()) + disk, err := s.get(ctx, azcli, diskId) if err != nil { err := fmt.Errorf("Unable to retrieve disk (%q): %s", s.SourceDiskResourceID, err) log.Printf("StepVerifySourceDisk.Run: error: %+v", err) @@ -64,7 +69,7 @@ func (s StepVerifySourceDisk) Run(ctx context.Context, state multistep.StateBag) return multistep.ActionHalt } - location := to.String(disk.Location) + location := 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.", @@ -80,4 +85,15 @@ func (s StepVerifySourceDisk) Run(ctx context.Context, state multistep.StateBag) return multistep.ActionContinue } +func (s StepVerifySourceDisk) getDisk(ctx context.Context, azcli client.AzureClientSet, id disks.DiskId) (*disks.Disk, error) { + diskResult, err := azcli.DisksClient().Get(ctx, id) + if err != nil { + return nil, err + } + if diskResult.Model == nil { + return nil, fmt.Errorf("SDK returned empty disk") + } + return diskResult.Model, nil +} + 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 index 84a360f3..eb4bab50 100644 --- a/builder/azure/chroot/step_verify_source_disk_test.go +++ b/builder/azure/chroot/step_verify_source_disk_test.go @@ -5,15 +5,12 @@ package chroot import ( "context" - "io/ioutil" - "net/http" + "fmt" "reflect" "regexp" - "strings" "testing" - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2021-11-01/compute" - "github.com/Azure/go-autorest/autorest" + "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-02/disks" "github.com/hashicorp/packer-plugin-azure/builder/azure/common/client" "github.com/hashicorp/packer-plugin-sdk/multistep" ) @@ -23,8 +20,11 @@ func Test_StepVerifySourceDisk_Run(t *testing.T) { SourceDiskResourceID string Location string - GetDiskResponseCode int - GetDiskResponseBody string + GetDiskError error + GetDiskResponse *disks.Disk + } + diskWithCorrectLocation := disks.Disk{ + Location: "westus2", } tests := []struct { name string @@ -38,8 +38,7 @@ func Test_StepVerifySourceDisk_Run(t *testing.T) { SourceDiskResourceID: "/subscriptions/subid1/resourcegroups/rg1/providers/Microsoft.Compute/disks/disk1", Location: "westus2", - GetDiskResponseCode: 200, - GetDiskResponseBody: `{"location":"westus2"}`, + GetDiskResponse: &diskWithCorrectLocation, }, want: multistep.ActionContinue, }, @@ -48,9 +47,6 @@ func Test_StepVerifySourceDisk_Run(t *testing.T) { fields: fields{ SourceDiskResourceID: "/other", Location: "westus2", - - GetDiskResponseCode: 200, - GetDiskResponseBody: `{"location":"westus2"}`, }, want: multistep.ActionHalt, errormatch: "Could not parse resource id", @@ -61,8 +57,8 @@ func Test_StepVerifySourceDisk_Run(t *testing.T) { SourceDiskResourceID: "/subscriptions/subid1/resourcegroups/rg1/providers/Microsoft.Compute/disks/disk1", Location: "westus2", - GetDiskResponseCode: 404, - GetDiskResponseBody: `{}`, + GetDiskError: fmt.Errorf("404"), + GetDiskResponse: nil, }, want: multistep.ActionHalt, errormatch: "Unable to retrieve", @@ -72,8 +68,6 @@ func Test_StepVerifySourceDisk_Run(t *testing.T) { fields: fields{ SourceDiskResourceID: "/subscriptions/subid1/resourcegroups/rg1/providers/Microsoft.Compute/images/image1", Location: "westus2", - - GetDiskResponseCode: 404, }, want: multistep.ActionHalt, errormatch: "not a managed disk", @@ -84,8 +78,7 @@ func Test_StepVerifySourceDisk_Run(t *testing.T) { SourceDiskResourceID: "/subscriptions/subid2/resourcegroups/rg1/providers/Microsoft.Compute/disks/disk1", Location: "westus2", - GetDiskResponseCode: 200, - GetDiskResponseBody: `{"location":"westus2"}`, + GetDiskResponse: &diskWithCorrectLocation, }, want: multistep.ActionHalt, errormatch: "different subscription", @@ -96,8 +89,7 @@ func Test_StepVerifySourceDisk_Run(t *testing.T) { SourceDiskResourceID: "/subscriptions/subid1/resourcegroups/rg1/providers/Microsoft.Compute/disks/disk1", Location: "eastus", - GetDiskResponseCode: 200, - GetDiskResponseBody: `{"location":"westus2"}`, + GetDiskResponse: &diskWithCorrectLocation, }, want: multistep.ActionHalt, errormatch: "different location", @@ -108,22 +100,18 @@ func Test_StepVerifySourceDisk_Run(t *testing.T) { s := StepVerifySourceDisk{ SourceDiskResourceID: tt.fields.SourceDiskResourceID, Location: tt.fields.Location, + get: func(ctx context.Context, azcli client.AzureClientSet, id disks.DiskId) (*disks.Disk, error) { + if tt.fields.GetDiskError == nil && tt.fields.GetDiskResponse == nil { + t.Fatalf("expected getDisk to not be called but it was") + } + return tt.fields.GetDiskResponse, tt.fields.GetDiskError + }, } - 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) diff --git a/builder/azure/common/artifact.go b/builder/azure/common/artifact.go index 44312a7b..2d17822d 100644 --- a/builder/azure/common/artifact.go +++ b/builder/azure/common/artifact.go @@ -10,10 +10,9 @@ import ( "sort" "strings" - "github.com/Azure/go-autorest/autorest/azure" + "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-01/images" "github.com/hashicorp/packer-plugin-azure/builder/azure/common/client" packersdk "github.com/hashicorp/packer-plugin-sdk/packer" - "github.com/hashicorp/packer-plugin-sdk/packer/registry/image" registryimage "github.com/hashicorp/packer-plugin-sdk/packer/registry/image" ) @@ -63,7 +62,7 @@ func (a *Artifact) String() string { } func (a *Artifact) State(name string) interface{} { - if name == image.ArtifactStateURI { + if name == registryimage.ArtifactStateURI { return a.hcpPackerRegistryMetadata() } return a.StateData[name] @@ -75,7 +74,7 @@ func (a *Artifact) Destroy() error { for _, resource := range a.Resources { log.Printf("Deleting resource %s", resource) - id, err := azure.ParseResourceID(resource) + id, err := client.ParseResourceID(resource) if err != nil { return fmt.Errorf("Unable to parse resource id (%s): %v", resource, err) } @@ -85,14 +84,10 @@ func (a *Artifact) Destroy() error { switch restype { case "microsoft.compute/images": - res, err := a.AzureClientSet.ImagesClient().Delete(ctx, id.ResourceGroup, id.ResourceName) + imageID := images.NewImageID(a.AzureClientSet.SubscriptionID(), id.ResourceGroup, id.ResourceName.String()) + err := a.AzureClientSet.ImagesClient().DeleteThenPoll(ctx, imageID) 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)) diff --git a/builder/azure/common/client/azure_authorizer.go b/builder/azure/common/client/azure_authorizer.go new file mode 100644 index 00000000..ce64db9c --- /dev/null +++ b/builder/azure/common/client/azure_authorizer.go @@ -0,0 +1,101 @@ +// Copyright (c) HashiCorp, Inc. + +// SPDX-License-Identifier: MPL-2.0 + +package client + +import ( + "context" + "fmt" + + jwt "github.com/golang-jwt/jwt" + "github.com/hashicorp/go-azure-sdk/sdk/auth" + "github.com/hashicorp/go-azure-sdk/sdk/environments" +) + +type NewSDKAuthOptions struct { + AuthType string + ClientID string + ClientSecret string + ClientJWT string + ClientCertPath string + TenantID string + SubscriptionID string +} + +func BuildResourceManagerAuthorizer(ctx context.Context, authOpts NewSDKAuthOptions, env environments.Environment) (auth.Authorizer, error) { + authorizer, err := buildAuthorizer(ctx, authOpts, env, env.ResourceManager) + if err != nil { + return nil, fmt.Errorf("building Resource Manager authorizer from credentials: %+v", err) + } + return authorizer, nil +} + +func BuildStorageAuthorizer(ctx context.Context, authOpts NewSDKAuthOptions, env environments.Environment) (auth.Authorizer, error) { + authorizer, err := buildAuthorizer(ctx, authOpts, env, env.Storage) + if err != nil { + return nil, fmt.Errorf("building Storage authorizer from credentials: %+v", err) + } + return authorizer, nil +} + +func buildAuthorizer(ctx context.Context, authOpts NewSDKAuthOptions, env environments.Environment, api environments.Api) (auth.Authorizer, error) { + var authConfig auth.Credentials + switch authOpts.AuthType { + case AuthTypeAzureCLI: + authConfig = auth.Credentials{ + Environment: env, + EnableAuthenticatingUsingAzureCLI: true, + } + case AuthTypeMSI: + authConfig = auth.Credentials{ + Environment: env, + EnableAuthenticatingUsingManagedIdentity: true, + } + case AuthTypeClientSecret: + authConfig = auth.Credentials{ + Environment: env, + EnableAuthenticatingUsingClientSecret: true, + ClientID: authOpts.ClientID, + ClientSecret: authOpts.ClientSecret, + TenantID: authOpts.TenantID, + } + case AuthTypeClientCert: + authConfig = auth.Credentials{ + Environment: env, + EnableAuthenticatingUsingClientCertificate: true, + ClientID: authOpts.ClientID, + ClientCertificatePath: authOpts.ClientCertPath, + ClientCertificatePassword: "", + } + case AuthTypeClientBearerJWT: + authConfig = auth.Credentials{ + Environment: env, + EnableAuthenticationUsingOIDC: true, + ClientID: authOpts.ClientID, + TenantID: authOpts.TenantID, + OIDCAssertionToken: authOpts.ClientJWT, + } + default: + panic("AuthType not set") + } + authorizer, err := auth.NewAuthorizerFromCredentials(ctx, authConfig, api) + if err != nil { + return nil, err + } + return authorizer, nil +} + +func GetObjectIdFromToken(token string) (string, error) { + claims := jwt.MapClaims{} + var p jwt.Parser + + var err error + + _, _, err = p.ParseUnverified(token, claims) + + if err != nil { + return "", err + } + return claims["oid"].(string), nil +} diff --git a/builder/azure/common/client/azure_client_set.go b/builder/azure/common/client/azure_client_set.go index 7c09562f..5bd10529 100644 --- a/builder/azure/common/client/azure_client_set.go +++ b/builder/azure/common/client/azure_client_set.go @@ -4,33 +4,41 @@ package client import ( + "context" + "fmt" "log" "net/http" + "regexp" + "strings" "time" "github.com/hashicorp/packer-plugin-sdk/useragent" - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2021-11-01/compute" - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2021-11-01/compute/computeapi" "github.com/Azure/go-autorest/autorest" + "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-01/images" + "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-01/virtualmachineimages" + "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-01/virtualmachines" + "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-02/disks" + "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-02/snapshots" + "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-03/galleryimages" + "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-03/galleryimageversions" + "github.com/hashicorp/go-azure-sdk/sdk/auth" + authWrapper "github.com/hashicorp/go-azure-sdk/sdk/auth/autorest" version "github.com/hashicorp/packer-plugin-azure/version" ) type AzureClientSet interface { MetadataClient() MetadataClientAPI - DisksClient() computeapi.DisksClientAPI - SnapshotsClient() computeapi.SnapshotsClientAPI - ImagesClient() computeapi.ImagesClientAPI + DisksClient() disks.DisksClient + SnapshotsClient() snapshots.SnapshotsClient + ImagesClient() images.ImagesClient - GalleryImagesClient() computeapi.GalleryImagesClientAPI - GalleryImageVersionsClient() computeapi.GalleryImageVersionsClientAPI + GalleryImagesClient() galleryimages.GalleryImagesClient + GalleryImageVersionsClient() galleryimageversions.GalleryImageVersionsClient - VirtualMachinesClient() computeapi.VirtualMachinesClientAPI - VirtualMachineImagesClient() VirtualMachineImagesClientAPI - VirtualMachineScaleSetVMsClient() computeapi.VirtualMachineScaleSetVMsClientAPI - - PollClient() autorest.Client + VirtualMachinesClient() virtualmachines.VirtualMachinesClient + VirtualMachineImagesClient() virtualmachineimages.VirtualMachineImagesClient // SubscriptionID returns the subscription ID that this client set was created for SubscriptionID() string @@ -39,10 +47,11 @@ type AzureClientSet interface { var _ AzureClientSet = &azureClientSet{} type azureClientSet struct { - sender autorest.Sender - authorizer autorest.Authorizer - subscriptionID string - PollingDelay time.Duration + sender autorest.Sender + authorizer auth.Authorizer + subscriptionID string + PollingDelay time.Duration + ResourceManagerEndpoint string } func New(c Config, say func(string)) (AzureClientSet, error) { @@ -50,15 +59,28 @@ func New(c Config, say func(string)) (AzureClientSet, error) { } func new(c Config, say func(string)) (*azureClientSet, error) { - token, err := c.GetServicePrincipalToken(say, c.CloudEnvironment().ResourceManagerEndpoint) + // Pass in relevant auth information for hashicorp/go-azure-sdk + authOptions := NewSDKAuthOptions{ + AuthType: c.AuthType(), + ClientID: c.ClientID, + ClientSecret: c.ClientSecret, + ClientJWT: c.ClientJWT, + ClientCertPath: c.ClientCertPath, + TenantID: c.TenantID, + SubscriptionID: c.SubscriptionID, + } + cloudEnv := c.cloudEnvironment + resourceManagerEndpoint, _ := cloudEnv.ResourceManager.Endpoint() + authorizer, err := BuildResourceManagerAuthorizer(context.TODO(), authOptions, *cloudEnv) if err != nil { return nil, err } return &azureClientSet{ - authorizer: autorest.NewBearerAuthorizer(token), - subscriptionID: c.SubscriptionID, - sender: http.DefaultClient, - PollingDelay: time.Second, + authorizer: authorizer, + subscriptionID: c.SubscriptionID, + sender: http.DefaultClient, + PollingDelay: time.Second, + ResourceManagerEndpoint: *resourceManagerEndpoint, }, nil } @@ -66,12 +88,12 @@ func (s azureClientSet) SubscriptionID() string { return s.subscriptionID } -func (s azureClientSet) configureAutorestClient(c *autorest.Client) { +func (s azureClientSet) configureTrack1Client(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.Authorizer = authWrapper.AutorestAuthorizer(s.authorizer) c.Sender = s.sender } @@ -82,65 +104,73 @@ func (s azureClientSet) MetadataClient() MetadataClientAPI { } } -func (s azureClientSet) DisksClient() computeapi.DisksClientAPI { - c := compute.NewDisksClient(s.subscriptionID) - s.configureAutorestClient(&c.Client) - c.PollingDelay = s.PollingDelay +func (s azureClientSet) DisksClient() disks.DisksClient { + c := disks.NewDisksClientWithBaseURI(s.ResourceManagerEndpoint) + s.configureTrack1Client(&c.Client) + c.Client.PollingDelay = s.PollingDelay return c } -func (s azureClientSet) SnapshotsClient() computeapi.SnapshotsClientAPI { - c := compute.NewSnapshotsClient(s.subscriptionID) - s.configureAutorestClient(&c.Client) - c.PollingDelay = s.PollingDelay +func (s azureClientSet) SnapshotsClient() snapshots.SnapshotsClient { + c := snapshots.NewSnapshotsClientWithBaseURI(s.ResourceManagerEndpoint) + s.configureTrack1Client(&c.Client) + c.Client.PollingDelay = s.PollingDelay return c } -func (s azureClientSet) ImagesClient() computeapi.ImagesClientAPI { - c := compute.NewImagesClient(s.subscriptionID) - s.configureAutorestClient(&c.Client) - c.PollingDelay = s.PollingDelay +func (s azureClientSet) ImagesClient() images.ImagesClient { + c := images.NewImagesClientWithBaseURI(s.ResourceManagerEndpoint) + s.configureTrack1Client(&c.Client) + c.Client.PollingDelay = s.PollingDelay return c } -func (s azureClientSet) VirtualMachinesClient() computeapi.VirtualMachinesClientAPI { - c := compute.NewVirtualMachinesClient(s.subscriptionID) - s.configureAutorestClient(&c.Client) - c.PollingDelay = s.PollingDelay +func (s azureClientSet) VirtualMachinesClient() virtualmachines.VirtualMachinesClient { + c := virtualmachines.NewVirtualMachinesClientWithBaseURI(s.ResourceManagerEndpoint) + s.configureTrack1Client(&c.Client) + c.Client.PollingDelay = s.PollingDelay return c } -func (s azureClientSet) VirtualMachineScaleSetVMsClient() computeapi.VirtualMachineScaleSetVMsClientAPI { - c := compute.NewVirtualMachineScaleSetVMsClient(s.subscriptionID) - s.configureAutorestClient(&c.Client) - c.PollingDelay = s.PollingDelay +func (s azureClientSet) VirtualMachineImagesClient() virtualmachineimages.VirtualMachineImagesClient { + c := virtualmachineimages.NewVirtualMachineImagesClientWithBaseURI(s.ResourceManagerEndpoint) + s.configureTrack1Client(&c.Client) + c.Client.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() galleryimages.GalleryImagesClient { + c := galleryimages.NewGalleryImagesClientWithBaseURI(s.ResourceManagerEndpoint) + s.configureTrack1Client(&c.Client) + c.Client.PollingDelay = s.PollingDelay + return c } -func (s azureClientSet) GalleryImagesClient() computeapi.GalleryImagesClientAPI { - c := compute.NewGalleryImagesClient(s.subscriptionID) - s.configureAutorestClient(&c.Client) - c.PollingDelay = s.PollingDelay +func (s azureClientSet) GalleryImageVersionsClient() galleryimageversions.GalleryImageVersionsClient { + c := galleryimageversions.NewGalleryImageVersionsClientWithBaseURI(s.ResourceManagerEndpoint) + s.configureTrack1Client(&c.Client) + c.Client.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 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 (s azureClientSet) PollClient() autorest.Client { - c := autorest.NewClientWithUserAgent("Packer-Azure-ClientSet") - s.configureAutorestClient(&c) - c.PollingDelay = time.Second * 5 - return c +var platformImageRegex = regexp.MustCompile(`^[-_.a-zA-Z0-9]+:[-_.a-zA-Z0-9]+:[-_.a-zA-Z0-9]+:[-_.a-zA-Z0-9]+$`) + +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/azure_client_set_mock.go b/builder/azure/common/client/azure_client_set_mock.go index f4ed2b8f..b03ba90e 100644 --- a/builder/azure/common/client/azure_client_set_mock.go +++ b/builder/azure/common/client/azure_client_set_mock.go @@ -4,73 +4,66 @@ package client import ( - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2021-11-01/compute/computeapi" - "github.com/Azure/go-autorest/autorest" + "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-01/images" + "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-01/virtualmachineimages" + "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-01/virtualmachines" + "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-02/disks" + "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-02/snapshots" + "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-03/galleryimages" + "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-03/galleryimageversions" ) 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 - VirtualMachineScaleSetVMsClientMock computeapi.VirtualMachineScaleSetVMsClientAPI - GalleryImagesClientMock computeapi.GalleryImagesClientAPI - GalleryImageVersionsClientMock computeapi.GalleryImageVersionsClientAPI - PollClientMock autorest.Client - MetadataClientMock MetadataClientAPI - SubscriptionIDMock string + DisksClientMock disks.DisksClient + SnapshotsClientMock snapshots.SnapshotsClient + ImagesClientMock images.ImagesClient + VirtualMachinesClientMock virtualmachines.VirtualMachinesClient + VirtualMachineImagesClientMock virtualmachineimages.VirtualMachineImagesClient + GalleryImagesClientMock galleryimages.GalleryImagesClient + GalleryImageVersionsClientMock galleryimageversions.GalleryImageVersionsClient + MetadataClientMock MetadataClientAPI + SubscriptionIDMock string } -// DisksClient returns a DisksClientAPI -func (m *AzureClientSetMock) DisksClient() computeapi.DisksClientAPI { +// DisksClient returns a DisksClient +func (m *AzureClientSetMock) DisksClient() disks.DisksClient { return m.DisksClientMock } -// SnapshotsClient returns a SnapshotsClientAPI -func (m *AzureClientSetMock) SnapshotsClient() computeapi.SnapshotsClientAPI { +// SnapshotsClient returns a SnapshotsClient +func (m *AzureClientSetMock) SnapshotsClient() snapshots.SnapshotsClient { return m.SnapshotsClientMock } -// ImagesClient returns a ImagesClientAPI -func (m *AzureClientSetMock) ImagesClient() computeapi.ImagesClientAPI { +// ImagesClient returns a ImagesClient +func (m *AzureClientSetMock) ImagesClient() images.ImagesClient { return m.ImagesClientMock } -// VirtualMachineImagesClient returns a VirtualMachineImagesClientAPI -func (m *AzureClientSetMock) VirtualMachineImagesClient() VirtualMachineImagesClientAPI { +// VirtualMachineImagesClient returns a VirtualMachineImagesClient +func (m *AzureClientSetMock) VirtualMachineImagesClient() virtualmachineimages.VirtualMachineImagesClient { return m.VirtualMachineImagesClientMock } -// VirtualMachinesClient returns a VirtualMachinesClientAPI -func (m *AzureClientSetMock) VirtualMachinesClient() computeapi.VirtualMachinesClientAPI { +// VirtualMachinesClient returns a VirtualMachinesClient +func (m *AzureClientSetMock) VirtualMachinesClient() virtualmachines.VirtualMachinesClient { return m.VirtualMachinesClientMock } -// VirtualMachineScaleSetVMsClient returns a VirtualMachineScaleSetVMsClientAPI -func (m *AzureClientSetMock) VirtualMachineScaleSetVMsClient() computeapi.VirtualMachineScaleSetVMsClientAPI { - return m.VirtualMachineScaleSetVMsClientMock -} - -// GalleryImagesClient returns a GalleryImagesClientAPI -func (m *AzureClientSetMock) GalleryImagesClient() computeapi.GalleryImagesClientAPI { +// GalleryImagesClient returns a GalleryImagesClient +func (m *AzureClientSetMock) GalleryImagesClient() galleryimages.GalleryImagesClient { return m.GalleryImagesClientMock } -// GalleryImageVersionsClient returns a GalleryImageVersionsClientAPI -func (m *AzureClientSetMock) GalleryImageVersionsClient() computeapi.GalleryImageVersionsClientAPI { +// GalleryImageVersionsClient returns a GalleryImageVersionsClient +func (m *AzureClientSetMock) GalleryImageVersionsClient() galleryimageversions.GalleryImageVersionsClient { 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 +// MetadataClient returns a MetadataClient func (m *AzureClientSetMock) MetadataClient() MetadataClientAPI { return m.MetadataClientMock } diff --git a/builder/azure/common/client/config.go b/builder/azure/common/client/config.go index af7d6562..323e207d 100644 --- a/builder/azure/common/client/config.go +++ b/builder/azure/common/client/config.go @@ -7,16 +7,19 @@ package client import ( "context" + "errors" "fmt" "log" + "net/http" "os" + "regexp" "strings" "time" - "github.com/Azure/go-autorest/autorest/adal" - "github.com/Azure/go-autorest/autorest/azure" + "github.com/Azure/go-autorest/autorest/azure/cli" jwt "github.com/golang-jwt/jwt" - "github.com/hashicorp/go-azure-helpers/authentication" + "github.com/hashicorp/go-azure-helpers/resourcemanager/commonids" + "github.com/hashicorp/go-azure-sdk/resource-manager/resources/2022-12-01/subscriptions" "github.com/hashicorp/go-azure-sdk/sdk/environments" packersdk "github.com/hashicorp/packer-plugin-sdk/packer" ) @@ -25,9 +28,8 @@ import ( // `client_id` and `subscription_id` are specified in addition to one and only // one of the following: `client_secret`, `client_jwt`, `client_cert_path` -- // Packer will use the specified Azure Active Directory (AAD) Service Principal -// (SP). If only `use_interactive_auth` 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 +// (SP). +// If none ofthese 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 with either a System Assigned Managed // Identity or User Assigned Managed Identity. @@ -36,8 +38,7 @@ type Config struct { // 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 - newCloudEnvironment *environments.Environment + cloudEnvironment *environments.Environment // The Hostname of the Azure Metadata Service // (for example management.azure.com), used to obtain the Cloud Environment // when using a Custom Azure Environment. This can also be sourced from the @@ -81,13 +82,12 @@ type Config struct { // 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"` - // Flag to use interactive login (use device code) authentication. Defaults to false. - // If enabled, it will use interactive authentication. - UseInteractiveAuth bool `mapstructure:"use_interactive_auth" required:"false"` } +// allow override for unit tests +var findTenantID = FindTenantID + const ( - AuthTypeDeviceLogin = "DeviceLogin" AuthTypeMSI = "ManagedIdentity" AuthTypeClientSecret = "ClientSecret" AuthTypeClientCert = "ClientCertificate" @@ -104,31 +104,24 @@ func (c *Config) SetDefaultValues() error { c.CloudEnvironmentName = DefaultCloudEnvironmentName } - err := c.setNewCloudEnvironment() - if err != nil { - return err - } return c.setCloudEnvironment() } -func (c *Config) CloudEnvironment() *azure.Environment { +func (c *Config) CloudEnvironment() *environments.Environment { return c.cloudEnvironment } -func (c *Config) NewCloudEnvironment() *environments.Environment { - return c.newCloudEnvironment -} func (c *Config) AuthType() string { return c.authType } -func (c *Config) setNewCloudEnvironment() error { +func (c *Config) setCloudEnvironment() error { if c.MetadataHost == "" { if v := os.Getenv("ARM_METADATA_URL"); v != "" { c.MetadataHost = v } } env, err := environments.FromEndpoint(context.TODO(), c.MetadataHost, c.CloudEnvironmentName) - c.newCloudEnvironment = env + c.cloudEnvironment = env if err != nil { // fall back to old method of normalizing and looking up cloud names. log.Printf(fmt.Sprintf("Error looking up environment using metadata host: %s. \n"+ @@ -157,57 +150,11 @@ func (c *Config) setNewCloudEnvironment() error { if err != nil { return err } - c.newCloudEnvironment = env + c.cloudEnvironment = env } return nil } -// This is still used by the Chroot and DTL builder -func (c *Config) setCloudEnvironment() error { - // First, try using the metadata host to look up the cloud. - if c.MetadataHost == "" { - if v := os.Getenv("ARM_METADATA_URL"); v != "" { - c.MetadataHost = v - } - } - - env, err := authentication.AzureEnvironmentByNameFromEndpoint(context.TODO(), c.MetadataHost, c.CloudEnvironmentName) - c.cloudEnvironment = env - - if err != nil { - // fall back to old method of normalizing and looking up cloud names. - log.Printf(fmt.Sprintf("Error looking up environment using metadata host: %s. \n"+ - "Falling back to hardcoded mechanism...", err.Error())) - lookup := map[string]string{ - "CHINA": "AzureChinaCloud", - "CHINACLOUD": "AzureChinaCloud", - "AZURECHINACLOUD": "AzureChinaCloud", - - "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) - if err != nil { - return err - } - c.cloudEnvironment = &env - } - - return nil -} - //nolint:ineffassign //this triggers a false positive because errs is passed by reference func (c Config) Validate(errs *packersdk.MultiError) { ///////////////////////////////////////////// @@ -230,10 +177,6 @@ func (c Config) Validate(errs *packersdk.MultiError) { return } - if c.useDeviceLogin() { - return - } - if c.SubscriptionID != "" && c.ClientID != "" && c.ClientSecret != "" && c.ClientCertPath == "" && @@ -286,104 +229,30 @@ func (c Config) Validate(errs *packersdk.MultiError) { " - client_secret\n"+ " - client_jwt\n"+ " - client_cert_path\n"+ - " - use_interactive_auth\n"+ " - use_azure_cli_auth\n"+ - " to use interactive user authentication, specify only the following fields:\n"+ - " - subscription_id\n"+ - " - use_interactive_auth\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.UseInteractiveAuth -} - func (c Config) UseCLI() bool { return c.UseAzureCLIAuth } func (c Config) UseMSI() bool { - return !c.UseInteractiveAuth && - !c.UseAzureCLIAuth && + return !c.UseAzureCLIAuth && 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, c.ClientID) - 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() { + if c.UseCLI() { c.authType = AuthTypeAzureCLI } else if c.UseMSI() { c.authType = AuthTypeMSI @@ -397,28 +266,19 @@ func (c *Config) FillParameters() error { } 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.newCloudEnvironment == nil { - newCloudErr := c.setNewCloudEnvironment() + newCloudErr := c.setCloudEnvironment() if newCloudErr != nil { return newCloudErr } } + if c.authType == AuthTypeAzureCLI { tenantID, subscriptionID, err := getIDsFromAzureCLI() if err != nil { @@ -429,7 +289,8 @@ func (c *Config) FillParameters() error { c.SubscriptionID = subscriptionID } - if c.TenantID == "" { + // CLI Auth does not require tenant, SDK parses that for us + if c.TenantID == "" && !c.UseAzureCLIAuth { tenantID, err := findTenantID(*c.cloudEnvironment, c.SubscriptionID) if err != nil { return err @@ -444,5 +305,55 @@ func (c *Config) FillParameters() error { return nil } -// allow override for unit tests -var findTenantID = FindTenantID +// 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") +} + +func FindTenantID(env environments.Environment, subscriptionID string) (string, error) { + const hdrKey = "WWW-Authenticate" + resourceManagerEndpoint, _ := env.ResourceManager.Endpoint() + c := subscriptions.NewSubscriptionsClientWithBaseURI(*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(), commonids.NewSubscriptionID(subscriptionID)) + if subs.HttpResponse == nil { + return "", fmt.Errorf("Request failed: %v", err) + } + + // Expecting 401 StatusUnauthorized here, just read the header + if subs.HttpResponse.StatusCode != http.StatusUnauthorized { + return "", fmt.Errorf("Unexpected response from Get Subscription: %v", err) + } + hdr := subs.HttpResponse.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/config_retriever_test.go b/builder/azure/common/client/config_retriever_test.go index 5be2a808..0a0d9f79 100644 --- a/builder/azure/common/client/config_retriever_test.go +++ b/builder/azure/common/client/config_retriever_test.go @@ -7,39 +7,38 @@ import ( "errors" "testing" - "github.com/Azure/go-autorest/autorest/azure" + "github.com/hashicorp/go-azure-sdk/sdk/environments" ) -func TestConfigRetrieverFillsTenantIDWhenEmpty(t *testing.T) { +func TestConfigRetrieverLeavesTenantIDWhenNotEmpty(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 } + 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 := retrievedTid; c.TenantID != expected { + if expected := userSpecifiedTid; c.TenantID != expected { t.Errorf("Expected TenantID to be %q but got %q", expected, c.TenantID) } } -func TestConfigRetrieverLeavesTenantIDWhenNotEmpty(t *testing.T) { +func TestConfigRetrieverFillsTenantIDWhenEmpty(t *testing.T) { c := Config{CloudEnvironmentName: "AzurePublicCloud"} - userSpecifiedTid := "not-empty" - c.TenantID = userSpecifiedTid + if expected := ""; c.TenantID != expected { + t.Errorf("Expected TenantID to be %q but got %q", expected, c.TenantID) + } - findTenantID = nil // assert that this not even called + retrievedTid := "my-tenant-id" + findTenantID = func(environments.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 := userSpecifiedTid; c.TenantID != expected { + if expected := retrievedTid; c.TenantID != expected { t.Errorf("Expected TenantID to be %q but got %q", expected, c.TenantID) } } @@ -49,9 +48,8 @@ func TestConfigRetrieverReturnsErrorWhenTenantIDEmptyAndRetrievalFails(t *testin 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) } + findTenantID = func(environments.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 index 3d9affc7..c6da3d93 100644 --- a/builder/azure/common/client/config_test.go +++ b/builder/azure/common/client/config_test.go @@ -8,14 +8,12 @@ import ( "crypto/rsa" "encoding/base64" "encoding/binary" - "fmt" "io" mrand "math/rand" "os" "testing" "time" - "github.com/Azure/go-autorest/autorest/azure" jwt "github.com/golang-jwt/jwt" "github.com/hashicorp/go-azure-sdk/sdk/environments" packersdk "github.com/hashicorp/packer-plugin-sdk/packer" @@ -47,13 +45,6 @@ func Test_ClientConfig_RequiredParametersSet(t *testing.T) { }, wantErr: false, }, - { - name: "use_interactive_auth will trigger device code flow", - config: Config{ - UseInteractiveAuth: true, - }, - wantErr: false, - }, { name: "client_id without client_secret, client_cert_path or client_jwt should not error", config: Config{ @@ -157,43 +148,13 @@ func Test_ClientConfig_RequiredParametersSet(t *testing.T) { } } -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(), - newCloudEnvironment: environments.AzurePublic(), + UseAzureCLIAuth: true, + cloudEnvironment: environments.AzurePublic(), } assertValid(t, cfg) @@ -207,96 +168,6 @@ func Test_ClientConfig_AzureCli(t *testing.T) { } } -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 == "" { @@ -305,36 +176,8 @@ func getEnvOrSkip(t *testing.T, envVar string) string { 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{ - UseInteractiveAuth: true, - SubscriptionID: "12345", - } - assertValid(t, cfg) - }) - t.Run("with TenantID", func(t *testing.T) { - cfg := Config{ - UseInteractiveAuth: true, - SubscriptionID: "12345", - TenantID: "12345", - } - assertValid(t, cfg) - }) -} - func assertValid(t *testing.T, cfg Config) { errs := &packersdk.MultiError{} cfg.Validate(errs) diff --git a/builder/azure/common/client/devicelogin.go b/builder/azure/common/client/devicelogin.go deleted file mode 100644 index 83e20e84..00000000 --- a/builder/azure/common/client/devicelogin.go +++ /dev/null @@ -1,211 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.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" - version "github.com/hashicorp/packer-plugin-azure/version" - "github.com/hashicorp/packer-plugin-sdk/useragent" -) - -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/platform_image.go b/builder/azure/common/client/platform_image.go deleted file mode 100644 index 1e6446af..00000000 --- a/builder/azure/common/client/platform_image.go +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package client - -import ( - "context" - "fmt" - "regexp" - "strings" - - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2021-11-01/compute" - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2021-11-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 678f64ee..00000000 --- a/builder/azure/common/client/platform_image_test.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.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/testclient.go b/builder/azure/common/client/testclient.go deleted file mode 100644 index b87a4a15..00000000 --- a/builder/azure/common/client/testclient.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.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 bb0fe7ff..00000000 --- a/builder/azure/common/client/tokenprovider.go +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.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 d148f811..00000000 --- a/builder/azure/common/client/tokenprovider_cert.go +++ /dev/null @@ -1,168 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.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/golang-jwt/jwt" - "github.com/hashicorp/packer-plugin-azure/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 603d872d..00000000 --- a/builder/azure/common/client/tokenprovider_cli.go +++ /dev/null @@ -1,165 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package client - -import ( - "bytes" - "context" - "encoding/json" - "errors" - "fmt" - "io/ioutil" - "os" - "path/filepath" - - "github.com/Azure/go-autorest/autorest/adal" - "github.com/Azure/go-autorest/autorest/azure" - "github.com/Azure/go-autorest/autorest/azure/cli" - "github.com/dimchansky/utfbom" - "github.com/mitchellh/go-homedir" -) - -// 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") -} - -const azureProfileJSON = "azureProfile.json" - -func configDir() string { - return os.Getenv("AZURE_CONFIG_DIR") -} - -// ProfilePath returns the path where the Azure Profile is stored from the Azure CLI -func ProfilePath() (string, error) { - if cfgDir := configDir(); cfgDir != "" { - return filepath.Join(cfgDir, azureProfileJSON), nil - } - return homedir.Expand("~/.azure/" + azureProfileJSON) -} - -// Profile represents a Profile from the Azure CLI -type Profile struct { - InstallationID string `json:"installationId"` - Subscriptions []Subscription `json:"subscriptions"` -} - -// Subscription represents a Subscription from the Azure CLI -type Subscription struct { - EnvironmentName string `json:"environmentName"` - ID string `json:"id"` - IsDefault bool `json:"isDefault"` - Name string `json:"name"` - State string `json:"state"` - TenantID string `json:"tenantId"` - User *User `json:"user"` -} - -// User represents a User from the Azure CLI -type User struct { - Name string `json:"name"` - Type string `json:"type"` -} - -// LoadProfile restores a Profile object from a file located at 'path'. -func LoadProfile(path string) (result Profile, err error) { - var contents []byte - contents, err = ioutil.ReadFile(path) - if err != nil { - err = fmt.Errorf("failed to open file (%s) while loading token: %v", path, err) - return - } - reader := utfbom.SkipOnly(bytes.NewReader(contents)) - - dec := json.NewDecoder(reader) - if err = dec.Decode(&result); err != nil { - err = fmt.Errorf("failed to decode contents of file (%s) into a Profile representation: %v", path, err) - return - } - - return -} diff --git a/builder/azure/common/client/tokenprovider_devicewflow.go b/builder/azure/common/client/tokenprovider_devicewflow.go deleted file mode 100644 index 150a8af0..00000000 --- a/builder/azure/common/client/tokenprovider_devicewflow.go +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.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 bd4236fc..00000000 --- a/builder/azure/common/client/tokenprovider_jwt.go +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.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 eb0f38be..00000000 --- a/builder/azure/common/client/tokenprovider_msi.go +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.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 - clientID string -} - -func NewMSIOAuthTokenProvider(env azure.Environment, clientID string) oAuthTokenProvider { - return &msiOAuthTokenProvider{env: env, clientID: clientID} -} - -func (tp *msiOAuthTokenProvider) getServicePrincipalToken() (*adal.ServicePrincipalToken, error) { - return tp.getServicePrincipalTokenWithResource(tp.env.ResourceManagerEndpoint) -} - -func (tp *msiOAuthTokenProvider) getServicePrincipalTokenWithResource(resource string) (*adal.ServicePrincipalToken, error) { - return adal.NewServicePrincipalTokenFromManagedIdentity(resource, &adal.ManagedIdentityOptions{ - ClientID: tp.clientID, - }) -} diff --git a/builder/azure/common/client/tokenprovider_secret.go b/builder/azure/common/client/tokenprovider_secret.go deleted file mode 100644 index 5cc56ee1..00000000 --- a/builder/azure/common/client/tokenprovider_secret.go +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.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 1be45074..00000000 --- a/builder/azure/common/client/tokenprovider_secret_test.go +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.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/vault.go b/builder/azure/common/vault.go deleted file mode 100644 index 4b1124f3..00000000 --- a/builder/azure/common/vault.go +++ /dev/null @@ -1,141 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.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 19061c32..00000000 --- a/builder/azure/common/vault_client_mock.go +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.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 53ad6701..00000000 --- a/builder/azure/common/vault_test.go +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.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/azure_client.go b/builder/azure/dtl/azure_client.go index 31460f34..d9fd844c 100644 --- a/builder/azure/dtl/azure_client.go +++ b/builder/azure/dtl/azure_client.go @@ -12,20 +12,17 @@ import ( "strconv" "time" - "github.com/golang-jwt/jwt" - "github.com/Azure/go-autorest/autorest" - hashiImagesSDK "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-01/images" - hashiVMSDK "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-01/virtualmachines" - hashiGalleryImagesSDK "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-03/galleryimages" - hashiGalleryImageVersionsSDK "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-03/galleryimageversions" - hashiDTLSDK "github.com/hashicorp/go-azure-sdk/resource-manager/devtestlab/2018-09-15" - hashiVaultsSDK "github.com/hashicorp/go-azure-sdk/resource-manager/keyvault/2023-02-01/vaults" - hashiNetworkSDK "github.com/hashicorp/go-azure-sdk/resource-manager/network/2022-09-01" - "github.com/hashicorp/go-azure-sdk/sdk/auth" + "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-01/images" + "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-03/galleryimages" + "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-03/galleryimageversions" + dtl "github.com/hashicorp/go-azure-sdk/resource-manager/devtestlab/2018-09-15" + "github.com/hashicorp/go-azure-sdk/resource-manager/keyvault/2023-02-01/vaults" + networks "github.com/hashicorp/go-azure-sdk/resource-manager/network/2022-09-01" authWrapper "github.com/hashicorp/go-azure-sdk/sdk/auth/autorest" "github.com/hashicorp/go-azure-sdk/sdk/client/resourcemanager" "github.com/hashicorp/go-azure-sdk/sdk/environments" + azcommon "github.com/hashicorp/packer-plugin-azure/builder/azure/common/client" "github.com/hashicorp/packer-plugin-azure/version" "github.com/hashicorp/packer-plugin-sdk/useragent" ) @@ -38,13 +35,12 @@ type AzureClient struct { InspectorMaxLength int LastError azureErrorResponse - hashiVMSDK.VirtualMachinesClient - hashiImagesSDK.ImagesClient - hashiVaultsSDK.VaultsClient - NetworkMetaClient hashiNetworkSDK.Client - hashiGalleryImageVersionsSDK.GalleryImageVersionsClient - hashiGalleryImagesSDK.GalleryImagesClient - DtlMetaClient hashiDTLSDK.Client + images.ImagesClient + vaults.VaultsClient + NetworkMetaClient networks.Client + galleryimageversions.GalleryImageVersionsClient + galleryimages.GalleryImagesClient + DtlMetaClient dtl.Client } func errorCapture(client *AzureClient) autorest.RespondDecorator { @@ -63,29 +59,17 @@ func errorCapture(client *AzureClient) autorest.RespondDecorator { } } -// 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. +// TODO Do we need a track 2 version of this method? func byConcatDecorators(decorators ...autorest.RespondDecorator) autorest.RespondDecorator { return func(r autorest.Responder) autorest.Responder { return autorest.DecorateResponder(r, decorators...) } } -type NewSDKAuthOptions struct { - AuthType string - ClientID string - ClientSecret string - ClientJWT string - ClientCertPath string - TenantID string - SubscriptionID string -} - // Returns an Azure Client used for the Azure Resource Manager // Also returns the Azure object ID for the authentication method used in the build func NewAzureClient(ctx context.Context, subscriptionID string, - cloud *environments.Environment, SharedGalleryTimeout time.Duration, CustomImageCaptureTimeout time.Duration, PollingDuration time.Duration, newSdkAuthOptions NewSDKAuthOptions) (*AzureClient, *string, error) { + cloud *environments.Environment, SharedGalleryTimeout time.Duration, CustomImageCaptureTimeout time.Duration, PollingDuration time.Duration, newSdkAuthOptions azcommon.NewSDKAuthOptions) (*AzureClient, *string, error) { var azureClient = &AzureClient{} @@ -96,11 +80,11 @@ func NewAzureClient(ctx context.Context, subscriptionID string, return nil, nil, fmt.Errorf("Azure Environment not configured correctly") } resourceManagerEndpoint, _ := cloud.ResourceManager.Endpoint() - resourceManagerAuthorizer, err := buildResourceManagerAuthorizer(ctx, newSdkAuthOptions, *cloud) + resourceManagerAuthorizer, err := azcommon.BuildResourceManagerAuthorizer(ctx, newSdkAuthOptions, *cloud) if err != nil { return nil, nil, err } - dtlMetaClient := hashiDTLSDK.NewClientWithBaseURI(*resourceManagerEndpoint, func(c *autorest.Client) { + dtlMetaClient := dtl.NewClientWithBaseURI(*resourceManagerEndpoint, func(c *autorest.Client) { c.Authorizer = authWrapper.AutorestAuthorizer(resourceManagerAuthorizer) c.UserAgent = fmt.Sprintf("%s %s", useragent.String(version.AzurePluginVersion.FormattedVersion()), "go-azure-sdk Meta Client") c.RequestInspector = withInspection(maxlen) @@ -108,21 +92,21 @@ func NewAzureClient(ctx context.Context, subscriptionID string, }) azureClient.DtlMetaClient = dtlMetaClient - azureClient.GalleryImageVersionsClient = hashiGalleryImageVersionsSDK.NewGalleryImageVersionsClientWithBaseURI(*resourceManagerEndpoint) + azureClient.GalleryImageVersionsClient = galleryimageversions.NewGalleryImageVersionsClientWithBaseURI(*resourceManagerEndpoint) azureClient.GalleryImageVersionsClient.Client.Authorizer = authWrapper.AutorestAuthorizer(resourceManagerAuthorizer) azureClient.GalleryImageVersionsClient.Client.RequestInspector = withInspection(maxlen) azureClient.GalleryImageVersionsClient.Client.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient)) azureClient.GalleryImageVersionsClient.Client.UserAgent = fmt.Sprintf("%s %s", useragent.String(version.AzurePluginVersion.FormattedVersion()), azureClient.GalleryImageVersionsClient.Client.UserAgent) azureClient.GalleryImageVersionsClient.Client.PollingDuration = PollingDuration - azureClient.GalleryImagesClient = hashiGalleryImagesSDK.NewGalleryImagesClientWithBaseURI(*resourceManagerEndpoint) + azureClient.GalleryImagesClient = galleryimages.NewGalleryImagesClientWithBaseURI(*resourceManagerEndpoint) azureClient.GalleryImagesClient.Client.Authorizer = authWrapper.AutorestAuthorizer(resourceManagerAuthorizer) azureClient.GalleryImagesClient.Client.RequestInspector = withInspection(maxlen) azureClient.GalleryImagesClient.Client.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient)) azureClient.GalleryImagesClient.Client.UserAgent = fmt.Sprintf("%s %s", useragent.String(version.AzurePluginVersion.FormattedVersion()), azureClient.GalleryImagesClient.Client.UserAgent) azureClient.GalleryImagesClient.Client.PollingDuration = PollingDuration - azureClient.ImagesClient = hashiImagesSDK.NewImagesClientWithBaseURI(*resourceManagerEndpoint) + azureClient.ImagesClient = images.NewImagesClientWithBaseURI(*resourceManagerEndpoint) azureClient.ImagesClient.Client.Authorizer = authWrapper.AutorestAuthorizer(resourceManagerAuthorizer) azureClient.ImagesClient.Client.RequestInspector = withInspection(maxlen) azureClient.ImagesClient.Client.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient)) @@ -130,7 +114,7 @@ func NewAzureClient(ctx context.Context, subscriptionID string, azureClient.ImagesClient.Client.PollingDuration = PollingDuration // TODO Request/Response inpectors for Track 2 - networkMetaClient, err := hashiNetworkSDK.NewClientWithBaseURI(cloud.ResourceManager, func(c *resourcemanager.Client) { + networkMetaClient, err := networks.NewClientWithBaseURI(cloud.ResourceManager, func(c *resourcemanager.Client) { c.Client.Authorizer = resourceManagerAuthorizer c.Client.UserAgent = "some-user-agent" }) @@ -143,7 +127,7 @@ func NewAzureClient(ctx context.Context, subscriptionID string, if err != nil { return nil, nil, err } - objectId, err := getObjectIdFromToken(token.AccessToken) + objectId, err := azcommon.GetObjectIdFromToken(token.AccessToken) if err != nil { return nil, nil, err } @@ -159,75 +143,6 @@ const ( AuthTypeAzureCLI = "AzureCLI" ) -func buildResourceManagerAuthorizer(ctx context.Context, authOpts NewSDKAuthOptions, env environments.Environment) (auth.Authorizer, error) { - authorizer, err := buildAuthorizer(ctx, authOpts, env, env.ResourceManager) - if err != nil { - return nil, fmt.Errorf("building Resource Manager authorizer from credentials: %+v", err) - } - return authorizer, nil -} - -func buildAuthorizer(ctx context.Context, authOpts NewSDKAuthOptions, env environments.Environment, api environments.Api) (auth.Authorizer, error) { - var authConfig auth.Credentials - switch authOpts.AuthType { - case AuthTypeDeviceLogin: - return nil, fmt.Errorf("DeviceLogin is not supported in v2 of the Azure Packer Plugin, however you can use the Azure CLI `az login --use-device-code` to use a device code, and then use CLI authentication") - case AuthTypeAzureCLI: - authConfig = auth.Credentials{ - Environment: env, - EnableAuthenticatingUsingAzureCLI: true, - } - case AuthTypeMSI: - authConfig = auth.Credentials{ - Environment: env, - EnableAuthenticatingUsingManagedIdentity: true, - } - case AuthTypeClientSecret: - authConfig = auth.Credentials{ - Environment: env, - EnableAuthenticatingUsingClientSecret: true, - ClientID: authOpts.ClientID, - ClientSecret: authOpts.ClientSecret, - TenantID: authOpts.TenantID, - } - case AuthTypeClientCert: - authConfig = auth.Credentials{ - Environment: env, - EnableAuthenticatingUsingClientCertificate: true, - ClientID: authOpts.ClientID, - ClientCertificatePath: authOpts.ClientCertPath, - ClientCertificatePassword: "", - } - case AuthTypeClientBearerJWT: - authConfig = auth.Credentials{ - Environment: env, - EnableAuthenticationUsingOIDC: true, - ClientID: authOpts.ClientID, - TenantID: authOpts.TenantID, - OIDCAssertionToken: authOpts.ClientJWT, - } - default: - panic("AuthType not set") - } - authorizer, err := auth.NewAuthorizerFromCredentials(ctx, authConfig, api) - if err != nil { - return nil, err - } - return authorizer, nil -} -func getObjectIdFromToken(token string) (string, error) { - claims := jwt.MapClaims{} - var p jwt.Parser - - var err error - - _, _, err = p.ParseUnverified(token, claims) - - if err != nil { - return "", err - } - return claims["oid"].(string), nil -} func getInspectorMaxLength() int64 { value, ok := os.LookupEnv(EnvPackerLogAzureMaxLen) if !ok { diff --git a/builder/azure/dtl/builder.go b/builder/azure/dtl/builder.go index 1d7096f7..b7d8453b 100644 --- a/builder/azure/dtl/builder.go +++ b/builder/azure/dtl/builder.go @@ -12,12 +12,13 @@ import ( "runtime" "strings" - hashiGalleryImagesSDK "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-03/galleryimages" - hashiDTLCustomImagesSDK "github.com/hashicorp/go-azure-sdk/resource-manager/devtestlab/2018-09-15/customimages" - hashiDTLLabsSDK "github.com/hashicorp/go-azure-sdk/resource-manager/devtestlab/2018-09-15/labs" - hashiDTLVNETSDK "github.com/hashicorp/go-azure-sdk/resource-manager/devtestlab/2018-09-15/virtualnetworks" + "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-03/galleryimages" + "github.com/hashicorp/go-azure-sdk/resource-manager/devtestlab/2018-09-15/customimages" + "github.com/hashicorp/go-azure-sdk/resource-manager/devtestlab/2018-09-15/labs" + "github.com/hashicorp/go-azure-sdk/resource-manager/devtestlab/2018-09-15/virtualnetworks" "github.com/hashicorp/hcl/v2/hcldec" packerAzureCommon "github.com/hashicorp/packer-plugin-azure/builder/azure/common" + commonclient "github.com/hashicorp/packer-plugin-azure/builder/azure/common/client" "github.com/hashicorp/packer-plugin-azure/builder/azure/common/constants" "github.com/hashicorp/packer-plugin-azure/builder/azure/common/lin" "github.com/hashicorp/packer-plugin-sdk/communicator" @@ -74,7 +75,7 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) b.stateBag.Put(constants.Ui, ui) // Pass in relevant auth information for hashicorp/go-azure-sdk - authOptions := NewSDKAuthOptions{ + authOptions := commonclient.NewSDKAuthOptions{ AuthType: b.config.ClientConfig.AuthType(), ClientID: b.config.ClientConfig.ClientID, ClientSecret: b.config.ClientConfig.ClientSecret, @@ -87,7 +88,7 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) azureClient, objectId, err := NewAzureClient( ctx, b.config.ClientConfig.SubscriptionID, - b.config.ClientConfig.NewCloudEnvironment(), + b.config.ClientConfig.CloudEnvironment(), b.config.SharedGalleryTimeout, b.config.CustomImageCaptureTimeout, b.config.PollingDurationTimeout, @@ -113,8 +114,8 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) 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 - customImageResourceId := hashiDTLCustomImagesSDK.NewCustomImageID(b.config.ClientConfig.SubscriptionID, b.config.ManagedImageResourceGroupName, b.config.LabName, b.config.ManagedImageName) - _, err = azureClient.DtlMetaClient.CustomImages.Get(ctx, customImageResourceId, hashiDTLCustomImagesSDK.DefaultGetOperationOptions()) + customImageResourceId := customimages.NewCustomImageID(b.config.ClientConfig.SubscriptionID, b.config.ManagedImageResourceGroupName, b.config.LabName, b.config.ManagedImageName) + _, err = azureClient.DtlMetaClient.CustomImages.Get(ctx, customImageResourceId, customimages.DefaultGetOperationOptions()) if err == nil { if b.config.PackerForce { @@ -145,7 +146,7 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) // For Managed Images, validate that Shared Gallery Image exists before publishing to SIG if b.config.isManagedImage() && b.config.SharedGalleryDestination.SigDestinationGalleryName != "" { sigSubscriptionID := b.stateBag.Get(constants.ArmSubscription).(string) - galleryId := hashiGalleryImagesSDK.NewGalleryImageID(sigSubscriptionID, b.config.SharedGalleryDestination.SigDestinationResourceGroup, b.config.SharedGalleryDestination.SigDestinationGalleryName, b.config.SharedGalleryDestination.SigDestinationImageName) + galleryId := galleryimages.NewGalleryImageID(sigSubscriptionID, b.config.SharedGalleryDestination.SigDestinationResourceGroup, b.config.SharedGalleryDestination.SigDestinationGalleryName, b.config.SharedGalleryDestination.SigDestinationImageName) _, err = azureClient.GalleryImagesClient.Get(ctx, galleryId) if err != nil { return nil, fmt.Errorf("the Shared Gallery Image '%s' to which to publish the managed image version to does not exist in the resource group '%s' or does not contain managed image '%s'", b.config.SharedGalleryDestination.SigDestinationGalleryName, b.config.SharedGalleryDestination.SigDestinationResourceGroup, b.config.SharedGalleryDestination.SigDestinationImageName) @@ -171,8 +172,8 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) } // Find the lab location - labResourceId := hashiDTLLabsSDK.NewLabID(b.config.ClientConfig.SubscriptionID, b.config.LabResourceGroupName, b.config.LabName) - lab, err := azureClient.DtlMetaClient.Labs.Get(ctx, labResourceId, hashiDTLLabsSDK.DefaultGetOperationOptions()) + labResourceId := labs.NewLabID(b.config.ClientConfig.SubscriptionID, b.config.LabResourceGroupName, b.config.LabName) + lab, err := azureClient.DtlMetaClient.Labs.Get(ctx, labResourceId, labs.DefaultGetOperationOptions()) if err != nil { return nil, fmt.Errorf("Unable to fetch the Lab %s information in %s resource group", b.config.LabName, b.config.LabResourceGroupName) } @@ -338,8 +339,8 @@ func (b *Builder) setTemplateParameters(stateBag multistep.StateBag) { func (b *Builder) getSubnetInformation(ctx context.Context, ui packersdk.Ui, azClient AzureClient) (*string, *string, error) { num := int64(10) - labResourceId := hashiDTLVNETSDK.NewLabID(b.config.ClientConfig.SubscriptionID, b.config.LabResourceGroupName, b.config.LabName) - virtualNetworkPage, err := azClient.DtlMetaClient.VirtualNetworks.List(ctx, labResourceId, hashiDTLVNETSDK.ListOperationOptions{Top: &num}) + labResourceId := virtualnetworks.NewLabID(b.config.ClientConfig.SubscriptionID, b.config.LabResourceGroupName, b.config.LabName) + virtualNetworkPage, err := azClient.DtlMetaClient.VirtualNetworks.List(ctx, labResourceId, virtualnetworks.ListOperationOptions{Top: &num}) if err != nil { return nil, nil, fmt.Errorf("Error retrieving Virtual networks in Resourcegroup %s", b.config.LabResourceGroupName) @@ -350,7 +351,7 @@ func (b *Builder) getSubnetInformation(ctx context.Context, ui packersdk.Ui, azC for _, subnetOverride := range *virtualNetwork.Properties.SubnetOverrides { // Check if the Subnet is allowed to create VMs having Public IP - if *subnetOverride.UseInVMCreationPermission == hashiDTLVNETSDK.UsagePermissionTypeAllow && *subnetOverride.UsePublicIPAddressPermission == hashiDTLVNETSDK.UsagePermissionTypeAllow { + if *subnetOverride.UseInVMCreationPermission == virtualnetworks.UsagePermissionTypeAllow && *subnetOverride.UsePublicIPAddressPermission == virtualnetworks.UsagePermissionTypeAllow { // 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. diff --git a/builder/azure/dtl/config.go b/builder/azure/dtl/config.go index d4968045..df129bd5 100644 --- a/builder/azure/dtl/config.go +++ b/builder/azure/dtl/config.go @@ -19,8 +19,9 @@ import ( "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/hashicorp/go-azure-sdk/resource-manager/compute/2021-07-01/compute" + "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-01/virtualmachines" + "github.com/masterzen/winrm" azcommon "github.com/hashicorp/packer-plugin-azure/builder/azure/common" @@ -212,7 +213,7 @@ type Config struct { // type for a managed image. Valid values are Standard_LRS and Premium_LRS. // The default is Standard_LRS. ManagedImageStorageAccountType string `mapstructure:"managed_image_storage_account_type" required:"false"` - managedImageStorageAccountType compute.StorageAccountTypes + managedImageStorageAccountType virtualmachines.StorageAccountTypes // the user can define up to 15 // tags. Tag names cannot exceed 512 characters, and tag values cannot exceed @@ -230,7 +231,7 @@ type Config struct { PlanID string `mapstructure:"plan_id" required:"false"` // 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) + // that value. // 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 @@ -264,7 +265,7 @@ type Config struct { // 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 + diskCachingType virtualmachines.CachingTypes // DTL values StorageType string `mapstructure:"storage_type"` @@ -329,11 +330,11 @@ func (c *Config) isPublishToSIG() bool { return c.SharedGalleryDestination.SigDestinationGalleryName != "" } -func (c *Config) toVirtualMachineCaptureParameters() *compute.VirtualMachineCaptureParameters { - return &compute.VirtualMachineCaptureParameters{ - DestinationContainerName: &c.CaptureContainerName, - VhdPrefix: &c.CaptureNamePrefix, - OverwriteVhds: to.BoolPtr(false), +func (c *Config) toVirtualMachineCaptureParameters() *virtualmachines.VirtualMachineCaptureParameters { + return &virtualmachines.VirtualMachineCaptureParameters{ + DestinationContainerName: c.CaptureContainerName, + VhdPrefix: c.CaptureNamePrefix, + OverwriteVhds: false, } } @@ -543,11 +544,11 @@ func provideDefaultValues(c *Config) error { } if c.ManagedImageStorageAccountType == "" { - c.managedImageStorageAccountType = compute.StorageAccountTypesStandardLRS + c.managedImageStorageAccountType = virtualmachines.StorageAccountTypesStandardLRS } if c.DiskCachingType == "" { - c.diskCachingType = compute.CachingTypesReadWrite + c.diskCachingType = virtualmachines.CachingTypesReadWrite } if c.ImagePublisher != "" && c.ImageVersion == "" { @@ -742,9 +743,9 @@ func assertRequiredParametersSet(c *Config, errs *packersdk.MultiError) { switch c.ManagedImageStorageAccountType { case "", string(compute.StorageAccountTypesStandardLRS): - c.managedImageStorageAccountType = compute.StorageAccountTypesStandardLRS + c.managedImageStorageAccountType = virtualmachines.StorageAccountTypesStandardLRS case string(compute.StorageAccountTypesPremiumLRS): - c.managedImageStorageAccountType = compute.StorageAccountTypesPremiumLRS + c.managedImageStorageAccountType = virtualmachines.StorageAccountTypesPremiumLRS default: errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("The managed_image_storage_account_type %q is invalid", c.ManagedImageStorageAccountType)) } diff --git a/builder/azure/dtl/config.hcl2spec.go b/builder/azure/dtl/config.hcl2spec.go index e5992e3d..0a956c1a 100644 --- a/builder/azure/dtl/config.hcl2spec.go +++ b/builder/azure/dtl/config.hcl2spec.go @@ -57,7 +57,6 @@ type FlatConfig struct { 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"` - UseInteractiveAuth *bool `mapstructure:"use_interactive_auth" required:"false" cty:"use_interactive_auth" hcl:"use_interactive_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"` @@ -176,7 +175,6 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "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}, - "use_interactive_auth": &hcldec.AttrSpec{Name: "use_interactive_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())}, diff --git a/builder/azure/dtl/config_test.go b/builder/azure/dtl/config_test.go index f8cf79ab..b8ca7b43 100644 --- a/builder/azure/dtl/config_test.go +++ b/builder/azure/dtl/config_test.go @@ -160,15 +160,15 @@ func TestConfigShouldTransformToVirtualMachineCaptureParameters(t *testing.T) { parameters := config.toVirtualMachineCaptureParameters() - if *parameters.DestinationContainerName != config.CaptureContainerName { - t.Errorf("Expected DestinationContainerName to be equal to config's CaptureContainerName, but they were '%s' and '%s' respectively.", *parameters.DestinationContainerName, config.CaptureContainerName) + if parameters.DestinationContainerName != config.CaptureContainerName { + t.Errorf("Expected DestinationContainerName to be equal to config's CaptureContainerName, but they were '%s' and '%s' respectively.", parameters.DestinationContainerName, config.CaptureContainerName) } - if *parameters.VhdPrefix != config.CaptureNamePrefix { - t.Errorf("Expected DestinationContainerName to be equal to config's CaptureContainerName, but they were '%s' and '%s' respectively.", *parameters.VhdPrefix, config.CaptureNamePrefix) + if parameters.VhdPrefix != config.CaptureNamePrefix { + t.Errorf("Expected DestinationContainerName to be equal to config's CaptureContainerName, but they were '%s' and '%s' respectively.", parameters.VhdPrefix, config.CaptureNamePrefix) } - if *parameters.OverwriteVhds != false { + if parameters.OverwriteVhds != false { t.Error("Expected OverwriteVhds to be false, but it was not.") } } diff --git a/builder/azure/dtl/step_capture_image.go b/builder/azure/dtl/step_capture_image.go index be575506..49c06938 100644 --- a/builder/azure/dtl/step_capture_image.go +++ b/builder/azure/dtl/step_capture_image.go @@ -7,7 +7,7 @@ import ( "context" "fmt" - hashiDTLCustomImagesSDK "github.com/hashicorp/go-azure-sdk/resource-manager/devtestlab/2018-09-15/customimages" + "github.com/hashicorp/go-azure-sdk/resource-manager/devtestlab/2018-09-15/customimages" "github.com/hashicorp/packer-plugin-azure/builder/azure/common/constants" "github.com/hashicorp/packer-plugin-sdk/multistep" packersdk "github.com/hashicorp/packer-plugin-sdk/packer" @@ -46,29 +46,29 @@ func (s *StepCaptureImage) captureImageFromVM(ctx context.Context) error { s.config.LabName, s.config.tmpComputeName) - customImageProperties := hashiDTLCustomImagesSDK.CustomImageProperties{} + customImageProperties := customimages.CustomImageProperties{} if s.config.OSType == constants.Target_Linux { - deprovision := hashiDTLCustomImagesSDK.LinuxOsStateDeprovisionRequested + deprovision := customimages.LinuxOsStateDeprovisionRequested if s.config.SkipSysprep { - deprovision = hashiDTLCustomImagesSDK.LinuxOsStateDeprovisionApplied + deprovision = customimages.LinuxOsStateDeprovisionApplied } - customImageProperties = hashiDTLCustomImagesSDK.CustomImageProperties{ - VM: &hashiDTLCustomImagesSDK.CustomImagePropertiesFromVM{ - LinuxOsInfo: &hashiDTLCustomImagesSDK.LinuxOsInfo{ + customImageProperties = customimages.CustomImageProperties{ + VM: &customimages.CustomImagePropertiesFromVM{ + LinuxOsInfo: &customimages.LinuxOsInfo{ LinuxOsState: &deprovision, }, SourceVMId: &imageID, }, } } else if s.config.OSType == constants.Target_Windows { - deprovision := hashiDTLCustomImagesSDK.WindowsOsStateSysprepRequested + deprovision := customimages.WindowsOsStateSysprepRequested if s.config.SkipSysprep { - deprovision = hashiDTLCustomImagesSDK.WindowsOsStateSysprepApplied + deprovision = customimages.WindowsOsStateSysprepApplied } - customImageProperties = hashiDTLCustomImagesSDK.CustomImageProperties{ - VM: &hashiDTLCustomImagesSDK.CustomImagePropertiesFromVM{ - WindowsOsInfo: &hashiDTLCustomImagesSDK.WindowsOsInfo{ + customImageProperties = customimages.CustomImageProperties{ + VM: &customimages.CustomImagePropertiesFromVM{ + WindowsOsInfo: &customimages.WindowsOsInfo{ WindowsOsState: &deprovision, }, SourceVMId: &imageID, @@ -76,12 +76,12 @@ func (s *StepCaptureImage) captureImageFromVM(ctx context.Context) error { } } - customImage := &hashiDTLCustomImagesSDK.CustomImage{ + customImage := &customimages.CustomImage{ Name: &s.config.ManagedImageName, Properties: customImageProperties, } - customImageId := hashiDTLCustomImagesSDK.NewCustomImageID(s.config.ClientConfig.SubscriptionID, s.config.LabResourceGroupName, s.config.LabName, s.config.ManagedImageName) + customImageId := customimages.NewCustomImageID(s.config.ClientConfig.SubscriptionID, s.config.LabResourceGroupName, s.config.LabName, s.config.ManagedImageName) err := s.client.DtlMetaClient.CustomImages.CreateOrUpdateThenPoll(ctx, customImageId, *customImage) if err != nil { s.say("Error from Capture Image") diff --git a/builder/azure/dtl/step_delete_virtual_machine.go b/builder/azure/dtl/step_delete_virtual_machine.go index 8f809e03..148b2f8e 100644 --- a/builder/azure/dtl/step_delete_virtual_machine.go +++ b/builder/azure/dtl/step_delete_virtual_machine.go @@ -7,7 +7,7 @@ import ( "context" "fmt" - hashiDTLVMSDK "github.com/hashicorp/go-azure-sdk/resource-manager/devtestlab/2018-09-15/virtualmachines" + dtlvirtualmachines "github.com/hashicorp/go-azure-sdk/resource-manager/devtestlab/2018-09-15/virtualmachines" "github.com/hashicorp/packer-plugin-azure/builder/azure/common/constants" "github.com/hashicorp/packer-plugin-sdk/multistep" packersdk "github.com/hashicorp/packer-plugin-sdk/packer" @@ -34,7 +34,7 @@ func NewStepDeleteVirtualMachine(client *AzureClient, ui packersdk.Ui, config *C } func (s *StepDeleteVirtualMachine) deleteVirtualMachine(ctx context.Context, subscriptionId string, labName string, resourceGroupName string, vmName string) error { - vmId := hashiDTLVMSDK.NewVirtualMachineID(subscriptionId, resourceGroupName, labName, vmName) + vmId := dtlvirtualmachines.NewVirtualMachineID(subscriptionId, resourceGroupName, labName, vmName) err := s.client.DtlMetaClient.VirtualMachines.DeleteThenPoll(ctx, vmId) if err != nil { s.say("Error from delete VM") diff --git a/builder/azure/dtl/step_deploy_template.go b/builder/azure/dtl/step_deploy_template.go index b0e13747..f6e4e8fe 100644 --- a/builder/azure/dtl/step_deploy_template.go +++ b/builder/azure/dtl/step_deploy_template.go @@ -11,8 +11,8 @@ import ( "github.com/hashicorp/go-azure-helpers/resourcemanager/commonids" - hashiLabsSDK "github.com/hashicorp/go-azure-sdk/resource-manager/devtestlab/2018-09-15/labs" - hashiDTLVMSDK "github.com/hashicorp/go-azure-sdk/resource-manager/devtestlab/2018-09-15/virtualmachines" + "github.com/hashicorp/go-azure-sdk/resource-manager/devtestlab/2018-09-15/labs" + "github.com/hashicorp/go-azure-sdk/resource-manager/devtestlab/2018-09-15/virtualmachines" "github.com/hashicorp/go-azure-sdk/resource-manager/network/2022-09-01/networkinterfaces" "github.com/hashicorp/packer-plugin-azure/builder/azure/common/constants" "github.com/hashicorp/packer-plugin-sdk/multistep" @@ -50,9 +50,9 @@ func (s *StepDeployTemplate) deployTemplate(ctx context.Context, resourceGroupNa labName := s.config.LabName // TODO Talk to Tom(s) about this, we have to have two different Labs IDs in different calls, so we can probably move this into the commonids package - labResourceId := hashiDTLVMSDK.NewLabID(subscriptionId, resourceGroupName, labName) - labId := hashiLabsSDK.NewLabID(subscriptionId, s.config.tmpResourceGroupName, labName) - vmlistPage, err := s.client.DtlMetaClient.VirtualMachines.List(ctx, labResourceId, hashiDTLVMSDK.DefaultListOperationOptions()) + labResourceId := virtualmachines.NewLabID(subscriptionId, resourceGroupName, labName) + labId := labs.NewLabID(subscriptionId, s.config.tmpResourceGroupName, labName) + vmlistPage, err := s.client.DtlMetaClient.VirtualMachines.List(ctx, labResourceId, virtualmachines.DefaultListOperationOptions()) if err != nil { s.say(s.client.LastError.Error()) return err @@ -78,8 +78,8 @@ func (s *StepDeployTemplate) deployTemplate(ctx context.Context, resourceGroupNa } expand := "Properties($expand=ComputeVm,Artifacts,NetworkInterface)" - vmResourceId := hashiDTLVMSDK.NewVirtualMachineID(subscriptionId, s.config.tmpResourceGroupName, labName, s.config.tmpComputeName) - vm, err := s.client.DtlMetaClient.VirtualMachines.Get(ctx, vmResourceId, hashiDTLVMSDK.GetOperationOptions{Expand: &expand}) + vmResourceId := virtualmachines.NewVirtualMachineID(subscriptionId, s.config.tmpResourceGroupName, labName, s.config.tmpComputeName) + vm, err := s.client.DtlMetaClient.VirtualMachines.Get(ctx, vmResourceId, virtualmachines.GetOperationOptions{Expand: &expand}) if err != nil { s.say(s.client.LastError.Error()) } @@ -113,19 +113,19 @@ func (s *StepDeployTemplate) deployTemplate(ctx context.Context, resourceGroupNa winrma) var hostname = "hostName" - dp := &hashiDTLVMSDK.ArtifactParameterProperties{} + dp := &virtualmachines.ArtifactParameterProperties{} dp.Name = &hostname dp.Value = &s.config.tmpFQDN - dparams := []hashiDTLVMSDK.ArtifactParameterProperties{*dp} + dparams := []virtualmachines.ArtifactParameterProperties{*dp} - winrmArtifact := &hashiDTLVMSDK.ArtifactInstallProperties{ + winrmArtifact := &virtualmachines.ArtifactInstallProperties{ ArtifactTitle: &winrma, ArtifactId: &artifactid, Parameters: &dparams, } - dtlArtifacts := []hashiDTLVMSDK.ArtifactInstallProperties{*winrmArtifact} - dtlArtifactsRequest := hashiDTLVMSDK.ApplyArtifactsRequest{Artifacts: &dtlArtifacts} + dtlArtifacts := []virtualmachines.ArtifactInstallProperties{*winrmArtifact} + dtlArtifactsRequest := virtualmachines.ApplyArtifactsRequest{Artifacts: &dtlArtifacts} // TODO this was an infinite loop, I have seen apply artifacts fail // But this needs a bit further validation into why it fails and diff --git a/builder/azure/dtl/step_power_off_compute.go b/builder/azure/dtl/step_power_off_compute.go index 497824d6..4ec364ba 100644 --- a/builder/azure/dtl/step_power_off_compute.go +++ b/builder/azure/dtl/step_power_off_compute.go @@ -7,7 +7,7 @@ import ( "context" "fmt" - hashiDTLVMSDK "github.com/hashicorp/go-azure-sdk/resource-manager/devtestlab/2018-09-15/virtualmachines" + "github.com/hashicorp/go-azure-sdk/resource-manager/devtestlab/2018-09-15/virtualmachines" "github.com/hashicorp/packer-plugin-azure/builder/azure/common/constants" "github.com/hashicorp/packer-plugin-sdk/multistep" packersdk "github.com/hashicorp/packer-plugin-sdk/packer" @@ -35,8 +35,7 @@ func NewStepPowerOffCompute(client *AzureClient, ui packersdk.Ui, config *Config } func (s *StepPowerOffCompute) powerOffCompute(ctx context.Context, resourceGroupName string, labName, computeName string) error { - //f, err := s.client.VirtualMachinesClient.Deallocate(ctx, resourceGroupName, computeName) - vmResourceId := hashiDTLVMSDK.NewVirtualMachineID(s.config.ClientConfig.SubscriptionID, s.config.tmpResourceGroupName, labName, computeName) + vmResourceId := virtualmachines.NewVirtualMachineID(s.config.ClientConfig.SubscriptionID, s.config.tmpResourceGroupName, labName, computeName) err := s.client.DtlMetaClient.VirtualMachines.StopThenPoll(ctx, vmResourceId) if err != nil { s.say(s.client.LastError.Error()) diff --git a/builder/azure/dtl/step_publish_to_shared_image_gallery.go b/builder/azure/dtl/step_publish_to_shared_image_gallery.go index 41450773..1ad778a9 100644 --- a/builder/azure/dtl/step_publish_to_shared_image_gallery.go +++ b/builder/azure/dtl/step_publish_to_shared_image_gallery.go @@ -7,7 +7,7 @@ import ( "context" "fmt" - hashiGalleryImageVersionsSDK "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-03/galleryimageversions" + "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-03/galleryimageversions" "github.com/hashicorp/packer-plugin-azure/builder/azure/common/constants" "github.com/hashicorp/packer-plugin-sdk/multistep" packersdk "github.com/hashicorp/packer-plugin-sdk/packer" @@ -41,35 +41,35 @@ func NewStepPublishToSharedImageGallery(client *AzureClient, ui packersdk.Ui, co func (s *StepPublishToSharedImageGallery) publishToSig(ctx context.Context, subscriptionID, managedImageID, sigDestinationResourceGroup, sigDestinationGalleryName, sigDestinationImageName, sigDestinationImageVersion string, sigReplicationRegions []string, location string, tags map[string]string) (string, error) { - replicationRegions := make([]hashiGalleryImageVersionsSDK.TargetRegion, len(sigReplicationRegions)) + replicationRegions := make([]galleryimageversions.TargetRegion, len(sigReplicationRegions)) for i, v := range sigReplicationRegions { regionName := v - replicationRegions[i] = hashiGalleryImageVersionsSDK.TargetRegion{Name: regionName} + replicationRegions[i] = galleryimageversions.TargetRegion{Name: regionName} } - galleryImageVersion := hashiGalleryImageVersionsSDK.GalleryImageVersion{ + galleryImageVersion := galleryimageversions.GalleryImageVersion{ Location: location, Tags: &tags, - Properties: &hashiGalleryImageVersionsSDK.GalleryImageVersionProperties{ - StorageProfile: hashiGalleryImageVersionsSDK.GalleryImageVersionStorageProfile{ - Source: &hashiGalleryImageVersionsSDK.GalleryArtifactVersionFullSource{ + Properties: &galleryimageversions.GalleryImageVersionProperties{ + StorageProfile: galleryimageversions.GalleryImageVersionStorageProfile{ + Source: &galleryimageversions.GalleryArtifactVersionFullSource{ Id: &managedImageID, }, }, - PublishingProfile: &hashiGalleryImageVersionsSDK.GalleryArtifactPublishingProfileBase{ + PublishingProfile: &galleryimageversions.GalleryArtifactPublishingProfileBase{ TargetRegions: &replicationRegions, }, }, } - galleryImageVersionId := hashiGalleryImageVersionsSDK.NewImageVersionID(subscriptionID, sigDestinationResourceGroup, sigDestinationGalleryName, sigDestinationImageName, sigDestinationImageVersion) + galleryImageVersionId := galleryimageversions.NewImageVersionID(subscriptionID, sigDestinationResourceGroup, sigDestinationGalleryName, sigDestinationImageName, sigDestinationImageVersion) err := s.client.GalleryImageVersionsClient.CreateOrUpdateThenPoll(ctx, galleryImageVersionId, galleryImageVersion) if err != nil { s.say(s.client.LastError.Error()) return "", err } - createdSIGImageVersion, err := s.client.GalleryImageVersionsClient.Get(ctx, galleryImageVersionId, hashiGalleryImageVersionsSDK.DefaultGetOperationOptions()) + createdSIGImageVersion, err := s.client.GalleryImageVersionsClient.Get(ctx, galleryImageVersionId, galleryimageversions.DefaultGetOperationOptions()) if err != nil { s.say(s.client.LastError.Error()) diff --git a/builder/azure/dtl/template_factory.go b/builder/azure/dtl/template_factory.go index 6fe6dd0c..872e690e 100644 --- a/builder/azure/dtl/template_factory.go +++ b/builder/azure/dtl/template_factory.go @@ -6,10 +6,10 @@ package dtl import ( "fmt" - hashiDTLLabsSDK "github.com/hashicorp/go-azure-sdk/resource-manager/devtestlab/2018-09-15/labs" + "github.com/hashicorp/go-azure-sdk/resource-manager/devtestlab/2018-09-15/labs" ) -type templateFactoryFuncDtl func(*Config) (*hashiDTLLabsSDK.LabVirtualMachineCreationParameter, error) +type templateFactoryFuncDtl func(*Config) (*labs.LabVirtualMachineCreationParameter, error) func newBool(val bool) *bool { b := true @@ -32,9 +32,9 @@ func getCustomImageId(config *Config) *string { return nil } -func GetVirtualMachineDeployment(config *Config) (*hashiDTLLabsSDK.LabVirtualMachineCreationParameter, error) { +func GetVirtualMachineDeployment(config *Config) (*labs.LabVirtualMachineCreationParameter, error) { - galleryImageRef := hashiDTLLabsSDK.GalleryImageReference{ + galleryImageRef := labs.GalleryImageReference{ Offer: &config.ImageOffer, Publisher: &config.ImagePublisher, Sku: &config.ImageSku, @@ -48,7 +48,7 @@ func GetVirtualMachineDeployment(config *Config) (*hashiDTLLabsSDK.LabVirtualMac config.LabName, config.LabVirtualNetworkName) - dtlArtifacts := []hashiDTLLabsSDK.ArtifactInstallProperties{} + dtlArtifacts := []labs.ArtifactInstallProperties{} if config.DtlArtifacts != nil { for i := range config.DtlArtifacts { @@ -62,16 +62,16 @@ func GetVirtualMachineDeployment(config *Config) (*hashiDTLLabsSDK.LabVirtualMac config.DtlArtifacts[i].RepositoryName, config.DtlArtifacts[i].ArtifactName) - dparams := []hashiDTLLabsSDK.ArtifactParameterProperties{} + dparams := []labs.ArtifactParameterProperties{} for j := range config.DtlArtifacts[i].Parameters { - dp := &hashiDTLLabsSDK.ArtifactParameterProperties{} + dp := &labs.ArtifactParameterProperties{} dp.Name = &config.DtlArtifacts[i].Parameters[j].Name dp.Value = &config.DtlArtifacts[i].Parameters[j].Value dparams = append(dparams, *dp) } - dtlArtifact := &hashiDTLLabsSDK.ArtifactInstallProperties{ + dtlArtifact := &labs.ArtifactInstallProperties{ ArtifactTitle: &config.DtlArtifacts[i].ArtifactName, ArtifactId: &config.DtlArtifacts[i].ArtifactId, Parameters: &dparams, @@ -80,7 +80,7 @@ func GetVirtualMachineDeployment(config *Config) (*hashiDTLLabsSDK.LabVirtualMac } } - labMachineProps := &hashiDTLLabsSDK.LabVirtualMachineCreationParameterProperties{ + labMachineProps := &labs.LabVirtualMachineCreationParameterProperties{ OwnerUserPrincipalName: &config.ClientConfig.ClientID, OwnerObjectId: &config.ClientConfig.ObjectID, Size: &config.VMSize, @@ -100,7 +100,7 @@ func GetVirtualMachineDeployment(config *Config) (*hashiDTLLabsSDK.LabVirtualMac Artifacts: &dtlArtifacts, } - labMachine := &hashiDTLLabsSDK.LabVirtualMachineCreationParameter{ + labMachine := &labs.LabVirtualMachineCreationParameter{ Name: &config.tmpComputeName, Location: &config.Location, // TODO diff --git a/builder/azure/pkcs12/pkcs12.go b/builder/azure/pkcs12/pkcs12.go index c2bf41a5..2f506529 100644 --- a/builder/azure/pkcs12/pkcs12.go +++ b/builder/azure/pkcs12/pkcs12.go @@ -250,9 +250,6 @@ func Decode(pfxData []byte, password string) (privateKey interface{}, certificat 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 } diff --git a/docs-partials/builder/azure/arm/Config-not-required.mdx b/docs-partials/builder/azure/arm/Config-not-required.mdx index c2f54da0..81076c9a 100644 --- a/docs-partials/builder/azure/arm/Config-not-required.mdx +++ b/docs-partials/builder/azure/arm/Config-not-required.mdx @@ -306,7 +306,7 @@ ``` - `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) + that value. 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 diff --git a/docs-partials/builder/azure/arm/Spot-not-required.mdx b/docs-partials/builder/azure/arm/Spot-not-required.mdx index d5671d16..6fb5da6b 100644 --- a/docs-partials/builder/azure/arm/Spot-not-required.mdx +++ b/docs-partials/builder/azure/arm/Spot-not-required.mdx @@ -1,6 +1,6 @@ -- `eviction_policy` (hashiVMSDK.VirtualMachineEvictionPolicyTypes) - Specify eviction policy for spot instance: "Deallocate" or "Delete". If this is set, a spot instance will be used. +- `eviction_policy` (virtualmachines.VirtualMachineEvictionPolicyTypes) - Specify eviction policy for spot instance: "Deallocate" or "Delete". If this is set, a spot instance will be used. - `max_price` (float32) - How much should the VM cost maximally per hour. Specify -1 (or do not specify) to not evict based on price. diff --git a/docs-partials/builder/azure/chroot/Config-not-required.mdx b/docs-partials/builder/azure/chroot/Config-not-required.mdx index 4508bd71..7e2a3d25 100644 --- a/docs-partials/builder/azure/chroot/Config-not-required.mdx +++ b/docs-partials/builder/azure/chroot/Config-not-required.mdx @@ -31,7 +31,7 @@ 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, +- `os_disk_size_gb` (int64) - 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) diff --git a/docs-partials/builder/azure/chroot/TargetRegion-not-required.mdx b/docs-partials/builder/azure/chroot/TargetRegion-not-required.mdx index d277b7f5..5fef0c8c 100644 --- a/docs-partials/builder/azure/chroot/TargetRegion-not-required.mdx +++ b/docs-partials/builder/azure/chroot/TargetRegion-not-required.mdx @@ -1,6 +1,6 @@ -- `replicas` (int32) - Number of replicas in this region. Default: 1 +- `replicas` (int64) - 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/docs-partials/builder/azure/common/client/Config-not-required.mdx b/docs-partials/builder/azure/common/client/Config-not-required.mdx index 36f0b1b4..ff2a7890 100644 --- a/docs-partials/builder/azure/common/client/Config-not-required.mdx +++ b/docs-partials/builder/azure/common/client/Config-not-required.mdx @@ -40,7 +40,4 @@ 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. -- `use_interactive_auth` (bool) - Flag to use interactive login (use device code) authentication. Defaults to false. - If enabled, it will use interactive authentication. - diff --git a/docs-partials/builder/azure/common/client/Config.mdx b/docs-partials/builder/azure/common/client/Config.mdx index cdb44a5b..523f83c4 100644 --- a/docs-partials/builder/azure/common/client/Config.mdx +++ b/docs-partials/builder/azure/common/client/Config.mdx @@ -4,9 +4,8 @@ Config allows for various ways to authenticate Azure clients. When `client_id` and `subscription_id` are specified in addition to one and only one of the following: `client_secret`, `client_jwt`, `client_cert_path` -- Packer will use the specified Azure Active Directory (AAD) Service Principal -(SP). If only `use_interactive_auth` 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 +(SP). +If none ofthese 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 with either a System Assigned Managed Identity or User Assigned Managed Identity. diff --git a/docs-partials/builder/azure/dtl/Config-not-required.mdx b/docs-partials/builder/azure/dtl/Config-not-required.mdx index 9c51166c..d3f43b2b 100644 --- a/docs-partials/builder/azure/dtl/Config-not-required.mdx +++ b/docs-partials/builder/azure/dtl/Config-not-required.mdx @@ -101,7 +101,7 @@ 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) + that value. 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 diff --git a/docs-partials/provisioner/azure-dtlartifact/Config-not-required.mdx b/docs-partials/provisioner/azure-dtlartifact/Config-not-required.mdx index 43911247..73a4894f 100644 --- a/docs-partials/provisioner/azure-dtlartifact/Config-not-required.mdx +++ b/docs-partials/provisioner/azure-dtlartifact/Config-not-required.mdx @@ -1,7 +1,7 @@ - `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) + that value. 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 diff --git a/docs/builders/arm.mdx b/docs/builders/arm.mdx index 4c2b1c0b..488099fa 100644 --- a/docs/builders/arm.mdx +++ b/docs/builders/arm.mdx @@ -44,12 +44,6 @@ You can use a different subscription if you set `subscription_id`. If your VM has multiple user assigned managed identities you will need to set `client_id` too. -#### Interactive User Authentication - -To use interactive user authentication, you should specify -`use_interactive_auth` only. Packer will use cached credentials or redirect you -to a website to log in. - #### Service Principal To use a [service principal](https://packer.io/docs/builders/azure#azure-active-directory-service-principal) diff --git a/docs/builders/index.mdx b/docs/builders/index.mdx index 8a5608a0..c8a3f607 100644 --- a/docs/builders/index.mdx +++ b/docs/builders/index.mdx @@ -35,15 +35,13 @@ builder](/packer/plugins/builders/azure/arm). It is much easier to use. 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. +-> **Don't know which authentication method to use?** + +It is reccomended to use 'use_azure_cli_auth' to enable Azure CLI authentication No matter which method you choose, the identity you use will need the appropriate permissions on Azure resources for Packer to operate. The minimal @@ -51,20 +49,6 @@ 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 `use_interactive_auth` 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 diff --git a/go.mod b/go.mod index eb3203df..e2b8b2f4 100644 --- a/go.mod +++ b/go.mod @@ -3,13 +3,9 @@ module github.com/hashicorp/packer-plugin-azure go 1.19 require ( - github.com/Azure/azure-sdk-for-go v66.0.0+incompatible github.com/Azure/go-autorest/autorest v0.11.29 - github.com/Azure/go-autorest/autorest/adal v0.9.23 - github.com/Azure/go-autorest/autorest/azure/auth v0.4.2 - github.com/Azure/go-autorest/autorest/azure/cli v0.4.4 + github.com/Azure/go-autorest/autorest/adal v0.9.23 // indirect github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect - github.com/Azure/go-autorest/autorest/to v0.4.0 github.com/approvals/go-approval-tests v0.0.0-20210131072903-38d0b0ec12b1 github.com/golang-jwt/jwt v3.2.2+incompatible github.com/google/go-cmp v0.5.9 @@ -25,9 +21,7 @@ require ( ) require ( - github.com/dimchansky/utfbom v1.1.1 github.com/hashicorp/go-azure-sdk v0.20230523.1140858 - github.com/mitchellh/go-homedir v1.1.0 github.com/tombuildsstuff/giovanni v0.20.0 ) @@ -37,6 +31,7 @@ require ( cloud.google.com/go/iam v0.6.0 // indirect cloud.google.com/go/storage v1.27.0 // indirect github.com/Azure/go-autorest v14.2.0+incompatible // indirect + github.com/Azure/go-autorest/autorest/azure/cli v0.4.6 // indirect github.com/Azure/go-autorest/autorest/validation v0.3.1 // indirect github.com/Azure/go-autorest/logger v0.2.1 // indirect github.com/Azure/go-autorest/tracing v0.6.0 // indirect @@ -50,6 +45,7 @@ require ( github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect github.com/cenkalti/backoff/v3 v3.2.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/dimchansky/utfbom v1.1.1 // indirect github.com/dylanmei/iso8601 v0.1.0 // indirect github.com/fatih/color v1.13.0 // indirect github.com/gofrs/flock v0.8.1 // indirect @@ -98,6 +94,7 @@ require ( github.com/mattn/go-isatty v0.0.14 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/go-fs v0.0.0-20180402235330-b7b9ca407fff // indirect + github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/go-testing-interface v1.14.1 // indirect github.com/mitchellh/go-wordwrap v1.0.1 // indirect github.com/mitchellh/iochan v1.0.0 // indirect diff --git a/go.sum b/go.sum index 060fd06d..bdc171cd 100644 --- a/go.sum +++ b/go.sum @@ -12,40 +12,27 @@ cloud.google.com/go/storage v1.27.0/go.mod h1:x9DOL8TK/ygDUMieqwfhdpQryTeEkhGKMi github.com/Azure/azure-sdk-for-go v45.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= github.com/Azure/azure-sdk-for-go v56.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= github.com/Azure/azure-sdk-for-go v66.0.0+incompatible h1:bmmC38SlE8/E81nNADlgmVGurPWMHDX2YNXVQMrBpEE= -github.com/Azure/azure-sdk-for-go v66.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= 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.11.3/go.mod h1:JFgpikqFJ/MleTTxwepExTKnFUKKszPS8UavbQYUMuw= github.com/Azure/go-autorest/autorest v0.11.19/go.mod h1:dSiJPy22c3u0OtOKDNttNgqpNFY/GeWa7GH/Pz56QRA= github.com/Azure/go-autorest/autorest v0.11.29 h1:I4+HL/JDvErx2LjyzaVxllw2lRDB5/BT2Bm4g20iqYw= github.com/Azure/go-autorest/autorest v0.11.29/go.mod h1:ZtEzC4Jy2JDrZLxvWs8LrBWEBycl1hbT1eknI8MtfAs= -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.9.0/go.mod h1:/c022QCutn2P7uY+/oQWWNcK9YU+MH96NgK+jErpbcg= 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/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M= github.com/Azure/go-autorest/autorest/adal v0.9.14/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M= +github.com/Azure/go-autorest/autorest/adal v0.9.18/go.mod h1:XVVeme+LZwABT8K5Lc3hA4nAe8LDBVle26gTrguhhPQ= github.com/Azure/go-autorest/autorest/adal v0.9.22/go.mod h1:XuAbAEUv2Tta//+voMI038TrJBqjKam0me7qR+L8Cmk= github.com/Azure/go-autorest/autorest/adal v0.9.23 h1:Yepx8CvFxwNKpH6ja7RZ+sKX+DWYNldbLiALMC3BTz8= github.com/Azure/go-autorest/autorest/adal v0.9.23/go.mod h1:5pcMqFkdPhviJdlEy3kC/v1ZLnQl0MH6XA5YCcMhy4c= -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/go.mod h1:ZG5p860J94/0kI9mNJVoIoLgXcirM2gF5i2kWloofxw= github.com/Azure/go-autorest/autorest/azure/cli v0.4.0/go.mod h1:JljT387FplPzBA31vUcvsetLKF3pec5bdAxjVU4kI2s= github.com/Azure/go-autorest/autorest/azure/cli v0.4.2/go.mod h1:7qkJkT+j6b+hIpzMOwPChJhTqS8VbsqqgULzMNRugoM= -github.com/Azure/go-autorest/autorest/azure/cli v0.4.4 h1:iuooz5cZL6VRcO7DVSFYxRcouqn6bFVE/e77Wts50Zk= -github.com/Azure/go-autorest/autorest/azure/cli v0.4.4/go.mod h1:yAQ2b6eP/CmLPnmLvxtT1ALIY3OR1oFcCqVBi8vHiTc= -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/go.mod h1:vcORJHLJEh643/Ioh9+vPmf1Ij9AEBM5FuBIXLmIy0g= +github.com/Azure/go-autorest/autorest/azure/cli v0.4.6 h1:w77/uPk80ZET2F+AfQExZyEWtn+0Rk/uw17m9fv5Ajc= +github.com/Azure/go-autorest/autorest/azure/cli v0.4.6/go.mod h1:piCfgPho7BiIDdEQ1+g4VmKyD5y+p/XtSNqE6Hc4QD0= 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/go.mod h1:a8FDP3DYzQ4RYfVAxAN3SVSiiO77gL2j2ronKKP0syM= github.com/Azure/go-autorest/autorest/mocks v0.4.0/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= github.com/Azure/go-autorest/autorest/mocks v0.4.2 h1:PGN4EDXnuQbojHbU0UWoNvmu9AGVwYHG9/fkDYhtAfw= @@ -55,11 +42,9 @@ github.com/Azure/go-autorest/autorest/to v0.4.0/go.mod h1:fE8iZBn7LQR7zH/9XU2NcP github.com/Azure/go-autorest/autorest/validation v0.3.0/go.mod h1:yhLgjC0Wda5DYXl6JAsWyUe4KVNffhoDhG0zVzUMo3E= 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/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc= github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= 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= @@ -114,6 +99,8 @@ github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6D github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= +github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/containerd/cgroups v0.0.0-20190919134610-bf292b21730f/go.mod h1:OApqhQ4XNSNC13gXIwDjhOQxjWa/NxkwZXJ1EvqT0ko= github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw= github.com/containerd/containerd v1.3.2/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= @@ -144,6 +131,7 @@ github.com/dylanmei/winrmtest v0.0.0-20170819153634-c2fbb09e6c08 h1:0bp6/GrNOrTD github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= @@ -179,6 +167,7 @@ github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXP github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= +github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= @@ -220,6 +209,7 @@ github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian/v3 v3.2.1 h1:d8MncMlErDFTwQGBK1xhv026j9kqhvw1Qv9IbWT1VLQ= +github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= @@ -522,7 +512,6 @@ golang.org/x/crypto v0.0.0-20190222235706-ffb98f73852f/go.mod h1:6SG95UA2DQfeDnf golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190418165655-df01cb2cc480/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= -golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= diff --git a/provisioner/azure-dtlartifact/provisioner.go b/provisioner/azure-dtlartifact/provisioner.go index d240fadc..2529c075 100644 --- a/provisioner/azure-dtlartifact/provisioner.go +++ b/provisioner/azure-dtlartifact/provisioner.go @@ -15,7 +15,7 @@ import ( "github.com/hashicorp/packer-plugin-azure/builder/azure/common/client" dtlBuilder "github.com/hashicorp/packer-plugin-azure/builder/azure/dtl" - hashiDTLVMSDK "github.com/hashicorp/go-azure-sdk/resource-manager/devtestlab/2018-09-15/virtualmachines" + "github.com/hashicorp/go-azure-sdk/resource-manager/devtestlab/2018-09-15/virtualmachines" packersdk "github.com/hashicorp/packer-plugin-sdk/packer" "github.com/hashicorp/packer-plugin-sdk/common" @@ -50,7 +50,7 @@ type Config struct { VMName string `mapstructure:"vm_name" required:"true"` // 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) + // that value. // 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 @@ -126,7 +126,7 @@ func (p *Provisioner) Provision(ctx context.Context, ui packersdk.Ui, comm packe } // Pass in relevant auth information for hashicorp/go-azure-sdk - authOptions := dtlBuilder.NewSDKAuthOptions{ + authOptions := client.NewSDKAuthOptions{ AuthType: p.config.ClientConfig.AuthType(), ClientID: p.config.ClientConfig.ClientID, ClientSecret: p.config.ClientConfig.ClientSecret, @@ -139,7 +139,7 @@ func (p *Provisioner) Provision(ctx context.Context, ui packersdk.Ui, comm packe azureClient, _, err := dtlBuilder.NewAzureClient( ctx, p.config.ClientConfig.SubscriptionID, - p.config.ClientConfig.NewCloudEnvironment(), + p.config.ClientConfig.CloudEnvironment(), p.config.PollingDurationTimeout, p.config.PollingDurationTimeout, p.config.PollingDurationTimeout, @@ -151,7 +151,7 @@ func (p *Provisioner) Provision(ctx context.Context, ui packersdk.Ui, comm packe } ui.Say("Installing Artifact DTL") - dtlArtifacts := []hashiDTLVMSDK.ArtifactInstallProperties{} + dtlArtifacts := []virtualmachines.ArtifactInstallProperties{} if p.config.DtlArtifacts != nil { for i := range p.config.DtlArtifacts { @@ -161,15 +161,15 @@ func (p *Provisioner) Provision(ctx context.Context, ui packersdk.Ui, comm packe p.config.LabName, p.config.DtlArtifacts[i].ArtifactName) - dparams := []hashiDTLVMSDK.ArtifactParameterProperties{} + dparams := []virtualmachines.ArtifactParameterProperties{} for j := range p.config.DtlArtifacts[i].Parameters { - dp := &hashiDTLVMSDK.ArtifactParameterProperties{} + dp := &virtualmachines.ArtifactParameterProperties{} dp.Name = &p.config.DtlArtifacts[i].Parameters[j].Name dp.Value = &p.config.DtlArtifacts[i].Parameters[j].Value dparams = append(dparams, *dp) } - Aip := hashiDTLVMSDK.ArtifactInstallProperties{ + Aip := virtualmachines.ArtifactInstallProperties{ ArtifactId: &p.config.DtlArtifacts[i].ArtifactId, Parameters: &dparams, ArtifactTitle: &p.config.DtlArtifacts[i].ArtifactName, @@ -178,13 +178,13 @@ func (p *Provisioner) Provision(ctx context.Context, ui packersdk.Ui, comm packe } } - dtlApplyArifactRequest := hashiDTLVMSDK.ApplyArtifactsRequest{ + dtlApplyArifactRequest := virtualmachines.ApplyArtifactsRequest{ Artifacts: &dtlArtifacts, } ui.Say("Applying artifact ") - vmResourceId := hashiDTLVMSDK.NewVirtualMachineID(p.config.ClientConfig.SubscriptionID, p.config.ResourceGroupName, p.config.LabName, p.config.VMName) + vmResourceId := virtualmachines.NewVirtualMachineID(p.config.ClientConfig.SubscriptionID, p.config.ResourceGroupName, p.config.LabName, p.config.VMName) err = azureClient.DtlMetaClient.VirtualMachines.ApplyArtifactsThenPoll(ctx, vmResourceId, dtlApplyArifactRequest) if err != nil { diff --git a/provisioner/azure-dtlartifact/provisioner.hcl2spec.go b/provisioner/azure-dtlartifact/provisioner.hcl2spec.go index da37b91a..1566c1c4 100644 --- a/provisioner/azure-dtlartifact/provisioner.hcl2spec.go +++ b/provisioner/azure-dtlartifact/provisioner.hcl2spec.go @@ -56,7 +56,6 @@ type FlatConfig struct { 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"` - UseInteractiveAuth *bool `mapstructure:"use_interactive_auth" required:"false" cty:"use_interactive_auth" hcl:"use_interactive_auth"` DtlArtifacts []FlatDtlArtifact `mapstructure:"dtl_artifacts" required:"true" cty:"dtl_artifacts" hcl:"dtl_artifacts"` LabName *string `mapstructure:"lab_name" required:"true" cty:"lab_name" hcl:"lab_name"` ResourceGroupName *string `mapstructure:"lab_resource_group_name" required:"true" cty:"lab_resource_group_name" hcl:"lab_resource_group_name"` @@ -97,7 +96,6 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "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}, - "use_interactive_auth": &hcldec.AttrSpec{Name: "use_interactive_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}, From 4d0c3ba54461350dbc6d7e57a242ca33e0c2c6e1 Mon Sep 17 00:00:00 2001 From: Jenna Goldstrich Date: Wed, 2 Aug 2023 17:03:03 -0700 Subject: [PATCH 07/15] Add WinRM secret expiration, Fast Fail on invalid SIG version, Use context timeout throughout codebase Reset readme, version file, and workflows to state in main origin --- .github/workflows/release.yml | 9 +-- .github/workflows/wip-test-oidc.yml | 55 ------------------- builder/azure/arm/azure_client.go | 41 +++++++++++--- builder/azure/arm/builder.go | 21 +++---- builder/azure/arm/builder_acc_test.go | 2 +- builder/azure/arm/builder_test.go | 7 +-- builder/azure/arm/config.go | 11 ++++ builder/azure/arm/config_test.go | 29 +++++++++- builder/azure/arm/inspector.go | 32 +++++++++++ builder/azure/arm/resource_resolver.go | 8 ++- builder/azure/arm/step_capture_image.go | 10 +++- builder/azure/arm/step_capture_image_test.go | 4 +- .../azure/arm/step_certificate_in_keyvault.go | 40 +++++++++----- .../azure/arm/step_create_resource_group.go | 6 +- .../arm/step_create_resource_group_test.go | 10 ++-- builder/azure/arm/step_deploy_template.go | 34 ++++++++---- .../azure/arm/step_deploy_template_test.go | 3 + builder/azure/arm/step_get_ip_address.go | 8 ++- builder/azure/arm/step_power_off_compute.go | 4 +- .../step_publish_to_shared_image_gallery.go | 6 +- ...ep_publish_to_shared_image_gallery_test.go | 2 +- builder/azure/arm/step_snapshot_data_disks.go | 6 +- .../arm/step_snapshot_data_disks_test.go | 6 +- builder/azure/arm/step_snapshot_os_disk.go | 6 +- .../azure/arm/step_snapshot_os_disk_test.go | 7 +-- builder/azure/chroot/step_create_image.go | 4 +- .../azure/chroot/step_create_new_diskset.go | 4 +- .../step_create_shared_image_version.go | 4 +- .../azure/chroot/step_create_snapshotset.go | 12 +++- builder/azure/common/artifact.go | 4 +- .../azure/common/client/azure_authorizer.go | 11 ++-- .../azure/common/client/azure_client_set.go | 26 +++++---- .../common/client/azure_client_set_mock.go | 7 +++ builder/azure/common/client/config.go | 4 +- builder/azure/common/constants/stateBag.go | 6 -- builder/azure/dtl/azure_client.go | 34 ++++++++++-- builder/azure/dtl/builder.go | 10 ++-- builder/azure/dtl/inspector.go | 32 +++++++++++ builder/azure/dtl/step_capture_image.go | 4 +- .../azure/dtl/step_delete_virtual_machine.go | 4 +- builder/azure/dtl/step_deploy_template.go | 16 +++--- builder/azure/dtl/step_power_off_compute.go | 4 +- .../step_publish_to_shared_image_gallery.go | 6 +- builder/azure/dtl/template_factory.go | 3 +- go.mod | 2 +- go.sum | 4 -- provisioner/azure-dtlartifact/provisioner.go | 6 +- 47 files changed, 363 insertions(+), 211 deletions(-) delete mode 100644 .github/workflows/wip-test-oidc.yml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5d807353..8c0734f4 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -48,12 +48,8 @@ jobs: - name: Describe plugin id: plugin_describe run: echo "api_version=$(go run . describe | jq -r '.api_version')" >> "$GITHUB_OUTPUT" - - name: Import GPG key - id: import_gpg - uses: crazy-max/ghaction-import-gpg@v5 - with: - gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }} - passphrase: ${{ secrets.GPG_PASSPHRASE }} + - name: Install signore + uses: hashicorp/setup-signore-package@v1 - name: Run GoReleaser uses: goreleaser/goreleaser-action@f82d6c1c344bcacabba2c841718984797f664a6b # v4.2.0 with: @@ -62,7 +58,6 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} API_VERSION: ${{ steps.plugin_describe.outputs.api_version }} - GPG_FINGERPRINT: ${{ steps.import_gpg.outputs.fingerprint }} SIGNORE_CLIENT_ID: ${{ secrets.SIGNORE_CLIENT_ID }} SIGNORE_CLIENT_SECRET: ${{ secrets.SIGNORE_CLIENT_SECRET }} SIGNORE_SIGNER: ${{ secrets.SIGNORE_SIGNER }} diff --git a/.github/workflows/wip-test-oidc.yml b/.github/workflows/wip-test-oidc.yml deleted file mode 100644 index 9cc95874..00000000 --- a/.github/workflows/wip-test-oidc.yml +++ /dev/null @@ -1,55 +0,0 @@ ---- -# taken and modified from https://github.com/hashicorp/go-azure-sdk/blob/main/.github/workflows/pr-acceptance-tests.yml -name: OIDC Example - Testing OIDC integration in the SDK branch -on: - push: - -permissions: - contents: read - id-token: write - -jobs: - secrets-check: - runs-on: ubuntu-latest - outputs: - available: "${{ steps.check-secrets.outputs.available }}" - steps: - # we check for the ACTIONS_ID_TOKEN_REQUEST_URL variable as a proxy for other secrets - # it will be unset when running for a PR from a fork - - id: check-secrets - run: | - if [[ "${ACTIONS_ID_TOKEN_REQUEST_URL}" == "" ]]; then - echo "available=false" | tee ${GITHUB_OUTPUT} - else - echo "available=true" | tee ${GITHUB_OUTPUT} - fi - - test-oidc: - runs-on: ubuntu-latest - needs: [secrets-check] - if: needs.secrets-check.outputs.available == 'true' - steps: - - name: Set OIDC Token - run: | - echo "ARM_OIDC_TOKEN=$(curl -H "Accept: application/json; api-version=2.0" -H "Authorization: Bearer ${ACTIONS_ID_TOKEN_REQUEST_TOKEN}" -H "Content-Type: application/json" -G --data-urlencode "audience=api://AzureADTokenExchange" "${ACTIONS_ID_TOKEN_REQUEST_URL}" | jq -r '.value')" >>${GITHUB_ENV} - - - name: Install Go - uses: actions/setup-go@4d34df0c2316fe8122ab82dc22947d607c0c91f9 # v4.0.0 - with: - go-version: '1.19.5' - - - name: Checkout - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 - - - name: Setup `packer` - uses: hashicorp/setup-packer@main - id: setup - - - name: Build the plugin - run: make - - - name: Try to run an AzureARM build with our OIDC token - run: packer build -force ./example/oidc-example.pkr.hcl - env: - ARM_CLIENT_ID: ${{ secrets.ARM_CLIENT_ID}} - ARM_SUBSCRIPTION_ID: ${{ secrets.ARM_SUBSCRIPTION_ID}} diff --git a/builder/azure/arm/azure_client.go b/builder/azure/arm/azure_client.go index a6ba1172..008d329f 100644 --- a/builder/azure/arm/azure_client.go +++ b/builder/azure/arm/azure_client.go @@ -28,6 +28,7 @@ import ( "github.com/hashicorp/go-azure-sdk/resource-manager/resources/2022-09-01/resourcegroups" "github.com/hashicorp/go-azure-sdk/resource-manager/storage/2022-09-01/storageaccounts" authWrapper "github.com/hashicorp/go-azure-sdk/sdk/auth/autorest" + "github.com/hashicorp/go-azure-sdk/sdk/client" "github.com/hashicorp/go-azure-sdk/sdk/client/resourcemanager" "github.com/hashicorp/go-azure-sdk/sdk/environments" commonclient "github.com/hashicorp/packer-plugin-azure/builder/azure/common/client" @@ -57,6 +58,9 @@ type AzureClient struct { GiovanniBlobClient giovanniBlobStorageSDK.Client InspectorMaxLength int LastError azureErrorResponse + + PollingDuration time.Duration + SharedGalleryTimeout time.Duration } func errorCapture(client *AzureClient) autorest.RespondDecorator { @@ -75,7 +79,19 @@ func errorCapture(client *AzureClient) autorest.RespondDecorator { } } -// TODO Do we need a track 2 version of this method? +func errorCaptureTrack2(client *AzureClient) client.ResponseMiddleware { + return func(req *http.Request, resp *http.Response) (*http.Response, error) { + body, bodyString := handleBody(resp.Body, math.MaxInt64) + resp.Body = body + + errorResponse := newAzureErrorResponse(bodyString) + if errorResponse != nil { + client.LastError = *errorResponse + } + return resp, nil + } +} + func byConcatDecorators(decorators ...autorest.RespondDecorator) autorest.RespondDecorator { return func(r autorest.Responder) autorest.Responder { return autorest.DecorateResponder(r, decorators...) @@ -84,20 +100,23 @@ func byConcatDecorators(decorators ...autorest.RespondDecorator) autorest.Respon // Returns an Azure Client used for the Azure Resource Manager // Also returns the Azure object ID for the authentication method used in the build -func NewAzureClient(ctx context.Context, isVHDBuild bool, cloud *environments.Environment, sharedGalleryTimeout time.Duration, pollingDuration time.Duration, newSdkAuthOptions commonclient.NewSDKAuthOptions) (*AzureClient, *string, error) { +func NewAzureClient(ctx context.Context, isVHDBuild bool, cloud *environments.Environment, sharedGalleryTimeout time.Duration, pollingDuration time.Duration, authOptions commonclient.AzureAuthOptions) (*AzureClient, *string, error) { var azureClient = &AzureClient{} - + azureClient.PollingDuration = pollingDuration + azureClient.SharedGalleryTimeout = sharedGalleryTimeout maxlen := getInspectorMaxLength() if cloud == nil || cloud.ResourceManager == nil { - // TODO Throw error message that helps users solve this problem return nil, nil, fmt.Errorf("azure environment not configured correctly") } resourceManagerEndpoint, _ := cloud.ResourceManager.Endpoint() - resourceManagerAuthorizer, err := commonclient.BuildResourceManagerAuthorizer(ctx, newSdkAuthOptions, *cloud) + resourceManagerAuthorizer, err := commonclient.BuildResourceManagerAuthorizer(ctx, authOptions, *cloud) if err != nil { return nil, nil, err } + + trackTwoResponseMiddleware := []client.ResponseMiddleware{byInspectingTrack2(maxlen), errorCaptureTrack2(azureClient)} + trackTwoRequestMiddleware := []client.RequestMiddleware{withInspectionTrack2(maxlen)} // Clients that have been ported to hashicorp/go-azure-sdk azureClient.DisksClient = disks.NewDisksClientWithBaseURI(*resourceManagerEndpoint) @@ -170,10 +189,11 @@ func NewAzureClient(ctx context.Context, isVHDBuild bool, cloud *environments.En azureClient.StorageAccountsClient.Client.UserAgent = fmt.Sprintf("%s %s", useragent.String(version.AzurePluginVersion.FormattedVersion()), azureClient.StorageAccountsClient.Client.UserAgent) azureClient.StorageAccountsClient.Client.PollingDuration = pollingDuration - // TODO Request/Response inpectors for Track 2 networkMetaClient, err := networks.NewClientWithBaseURI(cloud.ResourceManager, func(c *resourcemanager.Client) { c.Client.Authorizer = resourceManagerAuthorizer - c.Client.UserAgent = "some-user-agent" + c.Client.UserAgent = useragent.String(version.AzurePluginVersion.FormattedVersion()) + c.Client.ResponseMiddlewares = &trackTwoResponseMiddleware + c.Client.RequestMiddlewares = &trackTwoRequestMiddleware }) if err != nil { @@ -197,7 +217,7 @@ func NewAzureClient(ctx context.Context, isVHDBuild bool, cloud *environments.En // We only need the Blob Client to delete the OS VHD during VHD builds if isVHDBuild { - storageAccountAuthorizer, err := commonclient.BuildStorageAuthorizer(ctx, newSdkAuthOptions, *cloud) + storageAccountAuthorizer, err := commonclient.BuildStorageAuthorizer(ctx, authOptions, *cloud) if err != nil { return nil, nil, err } @@ -207,13 +227,16 @@ func NewAzureClient(ctx context.Context, isVHDBuild bool, cloud *environments.En azureClient.GiovanniBlobClient.Authorizer = authWrapper.AutorestAuthorizer(storageAccountAuthorizer) azureClient.GiovanniBlobClient.Client.RequestInspector = withInspection(maxlen) azureClient.GiovanniBlobClient.Client.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient)) + azureClient.GiovanniBlobClient.Client.PollingDelay = pollingDuration } token, err := resourceManagerAuthorizer.Token(ctx, &http.Request{}) if err != nil { return nil, nil, err } - // TODO Handle potential panic here if Access Token or child objects are null + if token == nil { + return nil, nil, fmt.Errorf("unable to parse token from Azure Resource Manager") + } objectId, err := commonclient.GetObjectIdFromToken(token.AccessToken) if err != nil { return nil, nil, err diff --git a/builder/azure/arm/builder.go b/builder/azure/arm/builder.go index 12801ce3..5c30e93c 100644 --- a/builder/azure/arm/builder.go +++ b/builder/azure/arm/builder.go @@ -12,7 +12,6 @@ import ( "os" "runtime" "strings" - "time" "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-01/images" "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-03/galleryimages" @@ -67,9 +66,6 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) ui.Say("Running builder ...") - ctx, cancel := context.WithTimeout(ctx, time.Minute*60) - defer cancel() - // FillParameters function captures authType and sets defaults. err := b.config.ClientConfig.FillParameters() if err != nil { @@ -91,7 +87,7 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) b.stateBag.Put(constants.Ui, ui) // Pass in relevant auth information for hashicorp/go-azure-sdk - authOptions := commonclient.NewSDKAuthOptions{ + authOptions := commonclient.AzureAuthOptions{ AuthType: b.config.ClientConfig.AuthType(), ClientID: b.config.ClientConfig.ClientID, ClientSecret: b.config.ClientConfig.ClientSecret, @@ -143,7 +139,9 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) 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)) - err := azureClient.ImagesClient.DeleteThenPoll(ctx, imageId) + deleteImageContext, cancel := context.WithTimeout(ctx, azureClient.PollingDuration) + defer cancel() + err := azureClient.ImagesClient.DeleteThenPoll(deleteImageContext, imageId) if err != nil { return nil, fmt.Errorf("failed to delete the managed image named %s : %s", b.config.ManagedImageName, azureClient.LastError.Error()) } @@ -286,7 +284,7 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) NewStepDeployTemplate(azureClient, ui, &b.config, keyVaultDeploymentName, GetCommunicatorSpecificKeyVaultDeployment, KeyVaultTemplate), ) } else if b.config.Comm.Type == "winrm" { - steps = append(steps, NewStepCertificateInKeyVault(azureClient, ui, &b.config, b.config.winrmCertificate)) + steps = append(steps, NewStepCertificateInKeyVault(azureClient, ui, &b.config, b.config.winrmCertificate, b.config.WinrmExpirationTime)) } else { privateKey, err := ssh.ParseRawPrivateKey(b.config.Comm.SSHPrivateKey) if err != nil { @@ -294,14 +292,14 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) } pk, ok := privateKey.(*rsa.PrivateKey) if !ok { - //https://learn.microsoft.com/en-us/azure/virtual-machines/windows/connect-ssh?tabs=azurecli#supported-ssh-key-formats + // https://learn.microsoft.com/en-us/azure/virtual-machines/windows/connect-ssh?tabs=azurecli#supported-ssh-key-formats return nil, errors.New("Provided private key must be in RSA format to use for SSH on Windows on Azure") } secret, err := b.config.formatCertificateForKeyVault(pk) if err != nil { return nil, err } - steps = append(steps, NewStepCertificateInKeyVault(azureClient, ui, &b.config, secret)) + steps = append(steps, NewStepCertificateInKeyVault(azureClient, ui, &b.config, secret, b.config.WinrmExpirationTime)) } steps = append(steps, NewStepGetCertificate(azureClient, ui), @@ -478,8 +476,7 @@ func (b *Builder) getBlobAccount(ctx context.Context, client *AzureClient, subsc 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.ArmNewSDKTags, b.config.AzureTags) + stateBag.Put(constants.ArmTags, b.config.AzureTags) stateBag.Put(constants.ArmComputeName, b.config.tmpComputeName) stateBag.Put(constants.ArmDeploymentName, b.config.tmpDeploymentName) @@ -541,7 +538,7 @@ func (b *Builder) setRuntimeParameters(stateBag multistep.StateBag) { } func (b *Builder) setTemplateParameters(stateBag multistep.StateBag) { - stateBag.Put(constants.ArmNewVirtualMachineCaptureParameters, b.config.toVirtualMachineCaptureParameters()) + stateBag.Put(constants.ArmVirtualMachineCaptureParameters, b.config.toVirtualMachineCaptureParameters()) } func (b *Builder) setImageParameters(stateBag multistep.StateBag) { diff --git a/builder/azure/arm/builder_acc_test.go b/builder/azure/arm/builder_acc_test.go index 95aea30b..7bc76411 100644 --- a/builder/azure/arm/builder_acc_test.go +++ b/builder/azure/arm/builder_acc_test.go @@ -374,7 +374,7 @@ func createTestAzureClient(t *testing.T) AzureClient { // Use CLI auth for our test client b.config.ClientConfig.UseAzureCLIAuth = true _ = b.config.ClientConfig.FillParameters() - authOptions := commonclient.NewSDKAuthOptions{ + authOptions := commonclient.AzureAuthOptions{ AuthType: b.config.ClientConfig.AuthType(), ClientID: b.config.ClientConfig.ClientID, ClientSecret: b.config.ClientConfig.ClientSecret, diff --git a/builder/azure/arm/builder_test.go b/builder/azure/arm/builder_test.go index 9206b7bc..3a9ca4d9 100644 --- a/builder/azure/arm/builder_test.go +++ b/builder/azure/arm/builder_test.go @@ -27,7 +27,6 @@ func TestStateBagShouldBePopulatedExpectedValues(t *testing.T) { constants.ArmNicName, constants.ArmResourceGroupName, constants.ArmStorageAccountName, - constants.ArmNewVirtualMachineCaptureParameters, constants.ArmPublicIPAddressName, constants.ArmAsyncResourceGroupDelete, } @@ -54,7 +53,7 @@ func TestStateBagShouldPoluateExpectedTags(t *testing.T) { t.Fatalf("failed to prepare: %s", err) } - tags, ok := testSubject.stateBag.Get(constants.ArmTags).(map[string]*string) + 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) } @@ -64,8 +63,8 @@ func TestStateBagShouldPoluateExpectedTags(t *testing.T) { } 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) + 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/config.go b/builder/azure/arm/config.go index f1c08ab8..aa3f226c 100644 --- a/builder/azure/arm/config.go +++ b/builder/azure/arm/config.go @@ -494,7 +494,13 @@ type Config struct { // automatically configure authentication credentials for the provisioned // machine. For Linux this configures an SSH authorized key. For Windows // this configures a WinRM certificate. + OSType string `mapstructure:"os_type" required:"false"` + + // A time duration with which to set the WinRM certificate to expire + // This only works for Windows builds (valid time units include `s` for seconds, `m` for + // minutes, and `h` for hours.) + WinrmExpirationTime time.Duration `mapstructure:"winrm_expiration_time" required:"false"` // 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. @@ -1239,6 +1245,11 @@ func assertRequiredParametersSet(c *Config, errs *packersdk.MultiError) { } if c.SharedGalleryDestination.SigDestinationImageVersion == "" { errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("An image_version must be specified for shared_image_gallery_destination")) + } else { + validImageVersion := regexp.MustCompile(`[0-9]*\.[0-9]*\.[0-9]*$`) + if !validImageVersion.Match([]byte(c.SharedGalleryDestination.SigDestinationImageVersion)) { + errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("An image_version must follow Major(int).Minor(int).Patch(int) format")) + } } if c.SharedGalleryDestination.SigDestinationSubscription == "" { c.SharedGalleryDestination.SigDestinationSubscription = c.ClientConfig.SubscriptionID diff --git a/builder/azure/arm/config_test.go b/builder/azure/arm/config_test.go index b4e96fc7..1662955d 100644 --- a/builder/azure/arm/config_test.go +++ b/builder/azure/arm/config_test.go @@ -1272,7 +1272,7 @@ func TestConfigShouldAcceptAbsentManagedImageButPresentSharedImageGalleryDestina "resource_group": "ignore", "gallery_name": "ignore", "image_name": "ignore", - "image_version": "ignore", + "image_version": "1.0.1", "replication_regions": "ignore", }, } @@ -2090,7 +2090,34 @@ func TestConfigShouldAllowSharedImageGalleryOptions(t *testing.T) { if err != nil { t.Errorf("expected config to accept Shared Image Gallery options - but failed with %q", err) } +} +func TestConfigShouldRejectSharedImageGalleryDestinationInvalidVersion(t *testing.T) { + config := map[string]interface{}{ + "location": "ignore", + "subscription_id": "ignore", + "os_type": "linux", + "image_sku": "ignore", + "image_offer": "ignore", + "image_publisher": "ignore", + "shared_image_gallery_destination": map[string]string{ + "resource_group": "ignore", + "gallery_name": "ignore", + "image_name": "ignore", + "image_version": "not semver", + "replication_regions": "ignore", + }, + } + + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) + if err == nil { + t.Fatal("expected config to reject invalid shared image gallery destination version", err) + } + errorMessage := "An image_version must follow Major(int).Minor(int).Patch(int) format" + if !strings.Contains(err.Error(), errorMessage) { + t.Errorf("expected config to reject with error containing %s but got %s", errorMessage, err) + } } func TestSharedImageGalleryWithSkipImageCreateOptions(t *testing.T) { diff --git a/builder/azure/arm/inspector.go b/builder/azure/arm/inspector.go index 222c3f63..6135ae97 100644 --- a/builder/azure/arm/inspector.go +++ b/builder/azure/arm/inspector.go @@ -13,6 +13,7 @@ import ( "github.com/Azure/go-autorest/autorest" "github.com/Azure/go-autorest/autorest/azure" + "github.com/hashicorp/go-azure-sdk/sdk/client" "github.com/hashicorp/packer-plugin-azure/builder/azure/common/logutil" ) @@ -55,6 +56,20 @@ func withInspection(maxlen int64) autorest.PrepareDecorator { } } +func withInspectionTrack2(maxlen int64) client.RequestMiddleware { + return 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 r, nil + } +} + func byInspecting(maxlen int64) autorest.RespondDecorator { return func(r autorest.Responder) autorest.Responder { return autorest.ResponderFunc(func(resp *http.Response) error { @@ -72,3 +87,20 @@ func byInspecting(maxlen int64) autorest.RespondDecorator { }) } } + +func byInspectingTrack2(maxlen int64) client.ResponseMiddleware { + return func(req *http.Request, resp *http.Response) (*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 resp, nil + } +} \ No newline at end of file diff --git a/builder/azure/arm/resource_resolver.go b/builder/azure/arm/resource_resolver.go index aab1cb7c..858968ee 100644 --- a/builder/azure/arm/resource_resolver.go +++ b/builder/azure/arm/resource_resolver.go @@ -93,7 +93,9 @@ func findManagedImageByName(client *AzureClient, name, subscriptionId, resourceG } func findVirtualNetworkResourceGroup(client *AzureClient, subscriptionId, name string) (string, error) { - virtualNetworks, err := client.NetworkMetaClient.VirtualNetworks.ListAllComplete(context.TODO(), commonids.NewSubscriptionID(subscriptionId)) + vnetListContext, cancel := context.WithTimeout(context.TODO(), client.PollingDuration) + defer cancel() + virtualNetworks, err := client.NetworkMetaClient.VirtualNetworks.ListAllComplete(vnetListContext, commonids.NewSubscriptionID(subscriptionId)) if err != nil { return "", err } @@ -119,7 +121,9 @@ func findVirtualNetworkResourceGroup(client *AzureClient, subscriptionId, name s func findVirtualNetworkSubnet(client *AzureClient, subscriptionId string, resourceGroupName string, name string) (string, error) { - subnets, err := client.NetworkMetaClient.Subnets.List(context.TODO(), subnets.NewVirtualNetworkID(subscriptionId, resourceGroupName, name)) + subnetListContext, cancel := context.WithTimeout(context.TODO(), client.PollingDuration) + defer cancel() + subnets, err := client.NetworkMetaClient.Subnets.List(subnetListContext, subnets.NewVirtualNetworkID(subscriptionId, resourceGroupName, name)) if err != nil { return "", err } diff --git a/builder/azure/arm/step_capture_image.go b/builder/azure/arm/step_capture_image.go index f7203e89..f83532f4 100644 --- a/builder/azure/arm/step_capture_image.go +++ b/builder/azure/arm/step_capture_image.go @@ -51,8 +51,10 @@ func (s *StepCaptureImage) generalize(ctx context.Context, vmId virtualmachines. } func (s *StepCaptureImage) captureImageFromVM(ctx context.Context, subscriptionId string, resourceGroupName string, imageName string, image *images.Image) error { + pollingContext, cancel := context.WithTimeout(ctx, s.client.PollingDuration) + defer cancel() id := images.NewImageID(subscriptionId, resourceGroupName, imageName) - err := s.client.ImagesClient.CreateOrUpdateThenPoll(ctx, id, *image) + err := s.client.ImagesClient.CreateOrUpdateThenPoll(pollingContext, id, *image) if err != nil { s.say(s.client.LastError.Error()) } @@ -60,7 +62,9 @@ func (s *StepCaptureImage) captureImageFromVM(ctx context.Context, subscriptionI } func (s *StepCaptureImage) captureImage(ctx context.Context, vmId virtualmachines.VirtualMachineId, parameters *virtualmachines.VirtualMachineCaptureParameters) error { - if err := s.client.VirtualMachinesClient.CaptureThenPoll(ctx, vmId, *parameters); err != nil { + pollingContext, cancel := context.WithTimeout(ctx, s.client.PollingDuration) + defer cancel() + if err := s.client.VirtualMachinesClient.CaptureThenPoll(pollingContext, vmId, *parameters); err != nil { s.say(s.client.LastError.Error()) return err } @@ -84,7 +88,7 @@ func (s *StepCaptureImage) Run(ctx context.Context, state multistep.StateBag) mu 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.ArmNewVirtualMachineCaptureParameters).(*virtualmachines.VirtualMachineCaptureParameters) + var vmCaptureParameters = state.Get(constants.ArmVirtualMachineCaptureParameters).(*virtualmachines.VirtualMachineCaptureParameters) var imageParameters = state.Get(constants.ArmImageParameters).(*images.Image) var subscriptionId = state.Get(constants.ArmSubscription).(string) var isManagedImage = state.Get(constants.ArmIsManagedImage).(bool) diff --git a/builder/azure/arm/step_capture_image_test.go b/builder/azure/arm/step_capture_image_test.go index 28ab8024..53cd4567 100644 --- a/builder/azure/arm/step_capture_image_test.go +++ b/builder/azure/arm/step_capture_image_test.go @@ -188,7 +188,7 @@ func TestStepCaptureImageShouldTakeStepArgumentsFromStateBag(t *testing.T) { var expectedComputeName = stateBag.Get(constants.ArmComputeName).(string) var expectedResourceGroupName = stateBag.Get(constants.ArmResourceGroupName).(string) - var expectedVirtualMachineCaptureParameters = stateBag.Get(constants.ArmNewVirtualMachineCaptureParameters).(*virtualmachines.VirtualMachineCaptureParameters) + var expectedVirtualMachineCaptureParameters = stateBag.Get(constants.ArmVirtualMachineCaptureParameters).(*virtualmachines.VirtualMachineCaptureParameters) actualVirtualMachineID := stateBag.Get(constants.ArmBuildVMInternalId).(string) if actualVirtualMachineID != expectedVirtualMachineID { @@ -215,7 +215,7 @@ func createTestStateBagStepCaptureImage() multistep.StateBag { stateBag.Put(constants.ArmComputeName, "Unit Test: ComputeName") stateBag.Put(constants.ArmResourceGroupName, "Unit Test: ResourceGroupName") stateBag.Put(constants.ArmSubscription, "Unit Test: SubscriptionId") - stateBag.Put(constants.ArmNewVirtualMachineCaptureParameters, &virtualmachines.VirtualMachineCaptureParameters{}) + stateBag.Put(constants.ArmVirtualMachineCaptureParameters, &virtualmachines.VirtualMachineCaptureParameters{}) stateBag.Put(constants.ArmIsManagedImage, false) stateBag.Put(constants.ArmManagedImageResourceGroupName, "") diff --git a/builder/azure/arm/step_certificate_in_keyvault.go b/builder/azure/arm/step_certificate_in_keyvault.go index 0ced001f..854cf758 100644 --- a/builder/azure/arm/step_certificate_in_keyvault.go +++ b/builder/azure/arm/step_certificate_in_keyvault.go @@ -6,6 +6,7 @@ package arm import ( "context" "fmt" + "time" "github.com/hashicorp/go-azure-sdk/resource-manager/keyvault/2023-02-01/secrets" "github.com/hashicorp/packer-plugin-azure/builder/azure/common/constants" @@ -14,21 +15,23 @@ import ( ) type StepCertificateInKeyVault struct { - config *Config - client *AzureClient - set func(ctx context.Context, id secrets.SecretId) error - say func(message string) - error func(e error) - certificate string + config *Config + client *AzureClient + set func(ctx context.Context, id secrets.SecretId) error + say func(message string) + error func(e error) + certificate string + expirationTime time.Duration } -func NewStepCertificateInKeyVault(client *AzureClient, ui packersdk.Ui, config *Config, certificate string) *StepCertificateInKeyVault { +func NewStepCertificateInKeyVault(client *AzureClient, ui packersdk.Ui, config *Config, certificate string, expirationTime time.Duration) *StepCertificateInKeyVault { var step = &StepCertificateInKeyVault{ - client: client, - config: config, - say: func(message string) { ui.Say(message) }, - error: func(e error) { ui.Error(e.Error()) }, - certificate: certificate, + client: client, + config: config, + say: func(message string) { ui.Say(message) }, + error: func(e error) { ui.Error(e.Error()) }, + certificate: certificate, + expirationTime: expirationTime, } step.set = step.setCertificate @@ -36,11 +39,20 @@ func NewStepCertificateInKeyVault(client *AzureClient, ui packersdk.Ui, config * } func (s *StepCertificateInKeyVault) setCertificate(ctx context.Context, id secrets.SecretId) error { - _, err := s.client.SecretsClient.CreateOrUpdate(ctx, id, secrets.SecretCreateOrUpdateParameters{ + secret := secrets.SecretCreateOrUpdateParameters{ Properties: secrets.SecretProperties{ Value: &s.certificate, }, - }) + } + if s.expirationTime != 0 { + // Secrets API expects expiration time in seconds since the start of the unix epoch + // https://learn.microsoft.com/en-us/azure/templates/microsoft.keyvault/vaults/secrets?pivots=deployment-language-bicep#secretattributes + expirationTimeUnix := time.Now().Add(s.expirationTime).Unix() + secret.Properties.Attributes = &secrets.Attributes{ + Exp: &expirationTimeUnix, + } + } + _, err := s.client.SecretsClient.CreateOrUpdate(ctx, id, secret) return err } diff --git a/builder/azure/arm/step_create_resource_group.go b/builder/azure/arm/step_create_resource_group.go index d34cc943..70839bbb 100644 --- a/builder/azure/arm/step_create_resource_group.go +++ b/builder/azure/arm/step_create_resource_group.go @@ -71,7 +71,7 @@ func (s *StepCreateResourceGroup) Run(ctx context.Context, state multistep.State var resourceGroupName = state.Get(constants.ArmResourceGroupName).(string) var location = state.Get(constants.ArmLocation).(string) - tags, ok := state.Get(constants.ArmNewSDKTags).(map[string]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) @@ -151,7 +151,9 @@ func (s *StepCreateResourceGroup) Cleanup(state multistep.StateBag) { } s.say(fmt.Sprintf("\n Not waiting for Resource Group delete as requested by user. Resource Group Name is %s", resourceGroupName)) } else { - err := s.client.ResourceGroupsClient.DeleteThenPoll(ctx, id, resourcegroups.DefaultDeleteOperationOptions()) + pollingContext, cancel := context.WithTimeout(ctx, s.client.PollingDuration) + defer cancel() + err := s.client.ResourceGroupsClient.DeleteThenPoll(pollingContext, id, resourcegroups.DefaultDeleteOperationOptions()) if err != nil { ui.Error(fmt.Sprintf("Error deleting resource group. Please delete it manually.\n\n"+ "Name: %s\n"+ diff --git a/builder/azure/arm/step_create_resource_group_test.go b/builder/azure/arm/step_create_resource_group_test.go index 9b9640ad..ecc2e9ee 100644 --- a/builder/azure/arm/step_create_resource_group_test.go +++ b/builder/azure/arm/step_create_resource_group_test.go @@ -131,7 +131,7 @@ func TestStepCreateResourceGroupShouldTakeStepArgumentsFromStateBag(t *testing.T var expectedResourceGroupName = stateBag.Get(constants.ArmResourceGroupName).(string) var expectedLocation = stateBag.Get(constants.ArmLocation).(string) var expectedSubscription = stateBag.Get(constants.ArmSubscription).(string) - var expectedTags = stateBag.Get(constants.ArmNewSDKTags).(map[string]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.") @@ -146,7 +146,7 @@ func TestStepCreateResourceGroupShouldTakeStepArgumentsFromStateBag(t *testing.T } if len(expectedTags) != len(actualTags) && expectedTags["tag01"] != actualTags["tag01"] { - t.Fatal("Expected the step to source 'constants.ArmNewSDKTags' from the state bag, but it did not.") + t.Fatal("Expected the step to source 'constants.ArmTags' from the state bag, but it did not.") } _, ok := stateBag.GetOk(constants.ArmIsResourceGroupCreated) @@ -212,7 +212,7 @@ func createTestStateBagStepCreateResourceGroup() multistep.StateBag { "tag01": value, } - stateBag.Put(constants.ArmNewSDKTags, tags) + stateBag.Put(constants.ArmTags, tags) return stateBag } @@ -229,7 +229,7 @@ func createTestExistingStateBagStepCreateResourceGroup() multistep.StateBag { "tag01": value, } - stateBag.Put(constants.ArmNewSDKTags, tags) + stateBag.Put(constants.ArmTags, tags) return stateBag } @@ -245,7 +245,7 @@ func TestStepCreateResourceGroupShouldFailIfTagsFailCast(t *testing.T) { "tag01": &value, } - stateBag.Put(constants.ArmNewSDKTags, tags) + stateBag.Put(constants.ArmTags, tags) var testSubject = &StepCreateResourceGroup{ create: func(context.Context, string, string, string, map[string]string) error { return nil }, say: func(message string) {}, diff --git a/builder/azure/arm/step_deploy_template.go b/builder/azure/arm/step_deploy_template.go index 325b179a..4f4125f7 100644 --- a/builder/azure/arm/step_deploy_template.go +++ b/builder/azure/arm/step_deploy_template.go @@ -156,8 +156,10 @@ func (s *StepDeployTemplate) deployTemplate(ctx context.Context, subscriptionId if err != nil { return err } + pollingContext, cancel := context.WithTimeout(ctx, s.client.PollingDuration) + defer cancel() id := deployments.NewResourceGroupProviderDeploymentID(subscriptionId, resourceGroupName, deploymentName) - err = s.client.DeploymentsClient.CreateOrUpdateThenPoll(ctx, id, *deployment) + err = s.client.DeploymentsClient.CreateOrUpdateThenPoll(pollingContext, id, *deployment) if err != nil { s.say(s.client.LastError.Error()) return err @@ -171,9 +173,11 @@ func (s *StepDeployTemplate) deleteDeploymentObject(ctx context.Context, state m subscriptionId := state.Get(constants.ArmSubscription).(string) ui := state.Get("ui").(packersdk.Ui) + pollingContext, cancel := context.WithTimeout(ctx, s.client.PollingDuration) + defer cancel() ui.Say(fmt.Sprintf("Removing the created Deployment object: '%s'", deploymentName)) id := deployments.NewResourceGroupProviderDeploymentID(subscriptionId, resourceGroupName, deploymentName) - err := s.client.DeploymentsClient.DeleteThenPoll(ctx, id) + err := s.client.DeploymentsClient.DeleteThenPoll(pollingContext, id) if err != nil { return err } @@ -194,7 +198,7 @@ func (s *StepDeployTemplate) getImageDetails(ctx context.Context, subscriptionId return "", "", err } if model := vm.Model; model == nil { - return "", "", errors.New("TODO") + return "", "", errors.New("SDK returned empty model") } if vm.Model.Properties.StorageProfile.OsDisk.Vhd != nil { imageType = "image" @@ -213,46 +217,52 @@ func (s *StepDeployTemplate) getImageDetails(ctx context.Context, subscriptionId } func deleteResource(ctx context.Context, client *AzureClient, subscriptionId string, resourceType string, resourceName string, resourceGroupName string) error { + + pollingContext, cancel := context.WithTimeout(ctx, client.PollingDuration) + defer cancel() + switch resourceType { case "Microsoft.Compute/virtualMachines": vmID := virtualmachines.NewVirtualMachineID(subscriptionId, resourceGroupName, resourceName) - if err := client.VirtualMachinesClient.DeleteThenPoll(ctx, vmID, virtualmachines.DefaultDeleteOperationOptions()); err != nil { + if err := client.VirtualMachinesClient.DeleteThenPoll(pollingContext, vmID, virtualmachines.DefaultDeleteOperationOptions()); err != nil { return err } case "Microsoft.KeyVault/vaults": id := commonids.NewKeyVaultID(subscriptionId, resourceGroupName, resourceName) - _, err := client.VaultsClient.Delete(ctx, id) + _, err := client.VaultsClient.Delete(pollingContext, id) return err case "Microsoft.Network/networkInterfaces": interfaceID := commonids.NewNetworkInterfaceID(subscriptionId, resourceGroupName, resourceName) - err := client.NetworkMetaClient.NetworkInterfaces.DeleteThenPoll(ctx, interfaceID) + err := client.NetworkMetaClient.NetworkInterfaces.DeleteThenPoll(pollingContext, interfaceID) return err case "Microsoft.Network/virtualNetworks": vnetID := virtualnetworks.NewVirtualNetworkID(subscriptionId, resourceGroupName, resourceName) - err := client.NetworkMetaClient.VirtualNetworks.DeleteThenPoll(ctx, vnetID) + err := client.NetworkMetaClient.VirtualNetworks.DeleteThenPoll(pollingContext, vnetID) return err case "Microsoft.Network/networkSecurityGroups": secGroupId := networksecuritygroups.NewNetworkSecurityGroupID(subscriptionId, resourceGroupName, resourceName) - err := client.NetworkMetaClient.NetworkSecurityGroups.DeleteThenPoll(ctx, secGroupId) + err := client.NetworkMetaClient.NetworkSecurityGroups.DeleteThenPoll(pollingContext, secGroupId) return err case "Microsoft.Network/publicIPAddresses": ipID := commonids.NewPublicIPAddressID(subscriptionId, resourceGroupName, resourceName) - err := client.NetworkMetaClient.PublicIPAddresses.DeleteThenPoll(ctx, ipID) + err := client.NetworkMetaClient.PublicIPAddresses.DeleteThenPoll(pollingContext, ipID) return err } return nil } -// TODO Let's split this into two seperate methods, right now its confusing, especially with the changes I'm making +// TODO Let's split this into two seperate methods // deleteVHD and deleteManagedDisk, and then just check in Cleanup which function to call func (s *StepDeployTemplate) deleteImage(ctx context.Context, imageName string, resourceGroupName string, isManagedDisk bool, subscriptionId string, storageAccountName string) error { // Managed disk + pollingContext, cancel := context.WithTimeout(ctx, s.client.PollingDuration) + defer cancel() if isManagedDisk { xs := strings.Split(imageName, "/") diskName := xs[len(xs)-1] diskId := disks.NewDiskID(subscriptionId, resourceGroupName, diskName) - if err := s.client.DisksClient.DeleteThenPoll(ctx, diskId); err != nil { + if err := s.client.DisksClient.DeleteThenPoll(pollingContext, diskId); err != nil { return err } return nil @@ -268,7 +278,7 @@ func (s *StepDeployTemplate) deleteImage(ctx context.Context, imageName string, if len(xs) < 3 { return errors.New("Unable to parse path of image " + imageName) } - _, err = s.client.GiovanniBlobClient.Delete(ctx, storageAccountName, "images", blobName, giovanniBlobStorageSDK.DeleteInput{}) + _, err = s.client.GiovanniBlobClient.Delete(pollingContext, storageAccountName, "images", blobName, giovanniBlobStorageSDK.DeleteInput{}) return err } diff --git a/builder/azure/arm/step_deploy_template_test.go b/builder/azure/arm/step_deploy_template_test.go index 110dc200..7f266869 100644 --- a/builder/azure/arm/step_deploy_template_test.go +++ b/builder/azure/arm/step_deploy_template_test.go @@ -7,6 +7,7 @@ import ( "context" "fmt" "testing" + "time" "github.com/hashicorp/packer-plugin-azure/builder/azure/common/constants" "github.com/hashicorp/packer-plugin-sdk/multistep" @@ -101,6 +102,7 @@ func TestStepDeployTemplateDeleteImageShouldFailWhenImageUrlCannotBeParsed(t *te error: func(e error) {}, name: "--deployment-name--", templateType: VirtualMachineTemplate, + client: &AzureClient{PollingDuration: time.Minute * 5}, } // Invalid URL per https://golang.org/src/net/url/url_test.go err := testSubject.deleteImage(context.TODO(), "http://[fe80::1%en0]/", "Unit Test: ResourceGroupName", false, "subscriptionId", "") @@ -113,6 +115,7 @@ func TestStepDeployTemplateDeleteImageShouldFailWithInvalidImage(t *testing.T) { var testSubject = &StepDeployTemplate{ say: func(message string) {}, error: func(e error) {}, + client: &AzureClient{PollingDuration: time.Minute * 5}, name: "--deployment-name--", templateType: VirtualMachineTemplate, } diff --git a/builder/azure/arm/step_get_ip_address.go b/builder/azure/arm/step_get_ip_address.go index 63cb7251..bda0a5b3 100644 --- a/builder/azure/arm/step_get_ip_address.go +++ b/builder/azure/arm/step_get_ip_address.go @@ -60,8 +60,10 @@ func NewStepGetIPAddress(client *AzureClient, ui packersdk.Ui, endpoint Endpoint } func (s *StepGetIPAddress) getPrivateIP(ctx context.Context, subscriptionId string, resourceGroupName string, ipAddressName string, interfaceName string) (string, error) { + getIPContext, cancel := context.WithTimeout(ctx, s.client.PollingDuration) + defer cancel() intID := commonids.NewNetworkInterfaceID(subscriptionId, resourceGroupName, interfaceName) - resp, err := s.client.NetworkMetaClient.NetworkInterfaces.Get(ctx, intID, networkinterfaces.DefaultGetOperationOptions()) + resp, err := s.client.NetworkMetaClient.NetworkInterfaces.Get(getIPContext, intID, networkinterfaces.DefaultGetOperationOptions()) if err != nil { s.say(s.client.LastError.Error()) return "", err @@ -71,8 +73,10 @@ func (s *StepGetIPAddress) getPrivateIP(ctx context.Context, subscriptionId stri } func (s *StepGetIPAddress) getPublicIP(ctx context.Context, subscriptionId string, resourceGroupName string, ipAddressName string, interfaceName string) (string, error) { + getIPContext, cancel := context.WithTimeout(ctx, s.client.PollingDuration) + defer cancel() ipID := commonids.NewPublicIPAddressID(subscriptionId, resourceGroupName, ipAddressName) - resp, err := s.client.NetworkMetaClient.PublicIPAddresses.Get(ctx, ipID, publicipaddresses.DefaultGetOperationOptions()) + resp, err := s.client.NetworkMetaClient.PublicIPAddresses.Get(getIPContext, ipID, publicipaddresses.DefaultGetOperationOptions()) if err != nil { return "", err } diff --git a/builder/azure/arm/step_power_off_compute.go b/builder/azure/arm/step_power_off_compute.go index 07e9b3fb..a478fd29 100644 --- a/builder/azure/arm/step_power_off_compute.go +++ b/builder/azure/arm/step_power_off_compute.go @@ -33,8 +33,10 @@ func NewStepPowerOffCompute(client *AzureClient, ui packersdk.Ui) *StepPowerOffC func (s *StepPowerOffCompute) powerOffCompute(ctx context.Context, subscriptionId string, resourceGroupName string, computeName string) error { hibernate := false + pollingContext, cancel := context.WithTimeout(ctx, s.client.PollingDuration) + defer cancel() vmId := virtualmachines.NewVirtualMachineID(subscriptionId, resourceGroupName, computeName) - err := s.client.VirtualMachinesClient.DeallocateThenPoll(ctx, vmId, virtualmachines.DeallocateOperationOptions{Hibernate: &hibernate}) + err := s.client.VirtualMachinesClient.DeallocateThenPoll(pollingContext, vmId, virtualmachines.DeallocateOperationOptions{Hibernate: &hibernate}) if err != nil { s.say(s.client.LastError.Error()) } diff --git a/builder/azure/arm/step_publish_to_shared_image_gallery.go b/builder/azure/arm/step_publish_to_shared_image_gallery.go index 35df0469..776988b4 100644 --- a/builder/azure/arm/step_publish_to_shared_image_gallery.go +++ b/builder/azure/arm/step_publish_to_shared_image_gallery.go @@ -114,8 +114,10 @@ func (s *StepPublishToSharedImageGallery) publishToSig(ctx context.Context, subs }, } + pollingContext, cancel := context.WithTimeout(ctx, s.client.SharedGalleryTimeout) + defer cancel() galleryImageVersionId := galleryimageversions.NewImageVersionID(subscriptionID, sharedImageGallery.SigDestinationResourceGroup, sharedImageGallery.SigDestinationGalleryName, sharedImageGallery.SigDestinationImageName, sharedImageGallery.SigDestinationImageVersion) - err = s.client.GalleryImageVersionsClient.CreateOrUpdateThenPoll(ctx, galleryImageVersionId, galleryImageVersion) + err = s.client.GalleryImageVersionsClient.CreateOrUpdateThenPoll(pollingContext, galleryImageVersionId, galleryImageVersion) if err != nil { s.say(s.client.LastError.Error()) return "", err @@ -140,7 +142,7 @@ func (s *StepPublishToSharedImageGallery) Run(ctx context.Context, stateBag mult s.say("Publishing to Shared Image Gallery ...") location := stateBag.Get(constants.ArmLocation).(string) - tags := stateBag.Get(constants.ArmNewSDKTags).(map[string]string) + tags := stateBag.Get(constants.ArmTags).(map[string]string) sharedImageGallery := getSigDestination(stateBag) var sourceID string 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 index 3ae8732b..c3dbe999 100644 --- a/builder/azure/arm/step_publish_to_shared_image_gallery_test.go +++ b/builder/azure/arm/step_publish_to_shared_image_gallery_test.go @@ -89,7 +89,7 @@ func createTestStateBagStepPublishToSharedImageGallery(managed bool) multistep.S tags := map[string]string{ "tag01": value, } - stateBag.Put(constants.ArmNewSDKTags, tags) + stateBag.Put(constants.ArmTags, tags) stateBag.Put(constants.ArmManagedImageSharedGalleryReplicationRegions, []string{"ManagedImageSharedGalleryReplicationRegionA", "ManagedImageSharedGalleryReplicationRegionB"}) stateBag.Put(constants.ArmManagedImageSharedGalleryImageVersionStorageAccountType, "Standard_LRS") if managed { diff --git a/builder/azure/arm/step_snapshot_data_disks.go b/builder/azure/arm/step_snapshot_data_disks.go index 4f1afb08..93ce63d9 100644 --- a/builder/azure/arm/step_snapshot_data_disks.go +++ b/builder/azure/arm/step_snapshot_data_disks.go @@ -46,8 +46,10 @@ func (s *StepSnapshotDataDisks) createDataDiskSnapshot(ctx context.Context, subs Location: *common.StringPtr(location), Tags: &tags, } + pollingContext, cancel := context.WithTimeout(ctx, s.client.PollingDuration) + defer cancel() id := snapshots.NewSnapshotID(subscriptionId, resourceGroupName, dstSnapshotName) - err := s.client.SnapshotsClient.CreateOrUpdateThenPoll(ctx, id, srcVhdToSnapshot) + err := s.client.SnapshotsClient.CreateOrUpdateThenPoll(pollingContext, id, srcVhdToSnapshot) if err != nil { s.say(s.client.LastError.Error()) @@ -71,7 +73,7 @@ func (s *StepSnapshotDataDisks) Run(ctx context.Context, stateBag multistep.Stat var resourceGroupName = stateBag.Get(constants.ArmManagedImageResourceGroupName).(string) var location = stateBag.Get(constants.ArmLocation).(string) - var tags = stateBag.Get(constants.ArmNewSDKTags).(map[string]string) + var tags = stateBag.Get(constants.ArmTags).(map[string]string) var additionalDisks = stateBag.Get(constants.ArmAdditionalDiskVhds).([]string) var dstSnapshotPrefix = stateBag.Get(constants.ArmManagedImageDataDiskSnapshotPrefix).(string) var subscriptionId = stateBag.Get(constants.ArmSubscription).(string) diff --git a/builder/azure/arm/step_snapshot_data_disks_test.go b/builder/azure/arm/step_snapshot_data_disks_test.go index 46803a67..0db20b5f 100644 --- a/builder/azure/arm/step_snapshot_data_disks_test.go +++ b/builder/azure/arm/step_snapshot_data_disks_test.go @@ -79,14 +79,10 @@ func createTestStateBagStepSnapshotDataDisks() multistep.StateBag { stateBag.Put(constants.ArmLocation, "Unit Test: Location") value := "Unit Test: Tags" - tags := map[string]*string{ - "tag01": &value, - } - newSDKTags := map[string]string{ + tags := map[string]string{ "tag02:": value, } stateBag.Put(constants.ArmTags, tags) - stateBag.Put(constants.ArmNewSDKTags, newSDKTags) stateBag.Put(constants.ArmAdditionalDiskVhds, []string{"subscriptions/123-456-789/resourceGroups/existingresourcegroup/providers/Microsoft.Compute/disks/osdisk"}) stateBag.Put(constants.ArmManagedImageDataDiskSnapshotPrefix, "Unit Test: ManagedImageDataDiskSnapshotPrefix") diff --git a/builder/azure/arm/step_snapshot_os_disk.go b/builder/azure/arm/step_snapshot_os_disk.go index 8d623a00..0aa99ac5 100644 --- a/builder/azure/arm/step_snapshot_os_disk.go +++ b/builder/azure/arm/step_snapshot_os_disk.go @@ -46,8 +46,10 @@ func (s *StepSnapshotOSDisk) createSnapshot(ctx context.Context, subscriptionId Location: *common.StringPtr(location), Tags: &tags, } + pollingContext, cancel := context.WithTimeout(ctx, s.client.PollingDuration) + defer cancel() id := snapshots.NewSnapshotID(subscriptionId, resourceGroupName, dstSnapshotName) - err := s.client.SnapshotsClient.CreateOrUpdateThenPoll(ctx, id, srcVhdToSnapshot) + err := s.client.SnapshotsClient.CreateOrUpdateThenPoll(pollingContext, id, srcVhdToSnapshot) if err != nil { s.say(s.client.LastError.Error()) @@ -73,7 +75,7 @@ func (s *StepSnapshotOSDisk) Run(ctx context.Context, stateBag multistep.StateBa var resourceGroupName = stateBag.Get(constants.ArmManagedImageResourceGroupName).(string) var location = stateBag.Get(constants.ArmLocation).(string) - var tags = stateBag.Get(constants.ArmNewSDKTags).(map[string]string) + var tags = stateBag.Get(constants.ArmTags).(map[string]string) var srcUriVhd = stateBag.Get(constants.ArmOSDiskUri).(string) var dstSnapshotName = stateBag.Get(constants.ArmManagedImageOSDiskSnapshotName).(string) var subscriptionId = stateBag.Get(constants.ArmSubscription).(string) diff --git a/builder/azure/arm/step_snapshot_os_disk_test.go b/builder/azure/arm/step_snapshot_os_disk_test.go index 5e7d4ab1..84500513 100644 --- a/builder/azure/arm/step_snapshot_os_disk_test.go +++ b/builder/azure/arm/step_snapshot_os_disk_test.go @@ -79,15 +79,10 @@ func createTestStateBagStepSnapshotOSDisk() multistep.StateBag { stateBag.Put(constants.ArmLocation, "Unit Test: Location") value := "Unit Test: Tags" - tags := map[string]*string{ - "tag01": &value, - } - - newSDKTags := map[string]string{ + tags := map[string]string{ "tag02:": value, } stateBag.Put(constants.ArmTags, tags) - stateBag.Put(constants.ArmNewSDKTags, newSDKTags) stateBag.Put(constants.ArmOSDiskUri, "subscriptions/123-456-789/resourceGroups/existingresourcegroup/providers/Microsoft.Compute/disks/osdisk") stateBag.Put(constants.ArmManagedImageOSDiskSnapshotName, "Unit Test: ManagedImageOSDiskSnapshotName") diff --git a/builder/azure/chroot/step_create_image.go b/builder/azure/chroot/step_create_image.go index babffe26..1763f19e 100644 --- a/builder/azure/chroot/step_create_image.go +++ b/builder/azure/chroot/step_create_image.go @@ -119,7 +119,9 @@ func (s *StepCreateImage) Run(ctx context.Context, state multistep.StateBag) mul } func (s *StepCreateImage) createImage(ctx context.Context, client client.AzureClientSet, id images.ImageId, image images.Image) error { - return client.ImagesClient().CreateOrUpdateThenPoll(ctx, id, image) + pollingContext, cancel := context.WithTimeout(ctx, client.PollingDelay()) + defer cancel() + return client.ImagesClient().CreateOrUpdateThenPoll(pollingContext, id, image) } func (*StepCreateImage) Cleanup(bag multistep.StateBag) {} // this is the final artifact, don't delete diff --git a/builder/azure/chroot/step_create_new_diskset.go b/builder/azure/chroot/step_create_new_diskset.go index ea5f77e8..e391116d 100644 --- a/builder/azure/chroot/step_create_new_diskset.go +++ b/builder/azure/chroot/step_create_new_diskset.go @@ -258,7 +258,9 @@ func (s *StepCreateNewDiskset) Cleanup(state multistep.StateBag) { ui.Say(fmt.Sprintf("Deleting disk %q", d)) diskID := disks.NewDiskID(azcli.SubscriptionID(), d.ResourceGroup, d.ResourceName.String()) - err = azcli.DisksClient().DeleteThenPoll(context.TODO(), diskID) + pollingContext, cancel := context.WithTimeout(context.TODO(), azcli.PollingDelay()) + defer cancel() + err = azcli.DisksClient().DeleteThenPoll(pollingContext, diskID) 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_shared_image_version.go b/builder/azure/chroot/step_create_shared_image_version.go index d3fb8c40..f68f0fae 100644 --- a/builder/azure/chroot/step_create_shared_image_version.go +++ b/builder/azure/chroot/step_create_shared_image_version.go @@ -116,8 +116,10 @@ func (s *StepCreateSharedImageVersion) Run(ctx context.Context, state multistep. } func (s *StepCreateSharedImageVersion) createImageVersion(ctx context.Context, azcli client.AzureClientSet, galleryImageVersionID galleryimageversions.ImageVersionId, imageVersion galleryimageversions.GalleryImageVersion) error { + pollingContext, cancel := context.WithTimeout(ctx, azcli.PollingDelay()) + defer cancel() return azcli.GalleryImageVersionsClient().CreateOrUpdateThenPoll( - ctx, + pollingContext, galleryImageVersionID, imageVersion) } diff --git a/builder/azure/chroot/step_create_snapshotset.go b/builder/azure/chroot/step_create_snapshotset.go index 79f650ea..7d4aeffc 100644 --- a/builder/azure/chroot/step_create_snapshotset.go +++ b/builder/azure/chroot/step_create_snapshotset.go @@ -90,7 +90,9 @@ func (s *StepCreateSnapshotset) Run(ctx context.Context, state multistep.StateBa } func (s *StepCreateSnapshotset) createSnapshot(ctx context.Context, azcli client.AzureClientSet, id snapshots.SnapshotId, snapshot snapshots.Snapshot) error { - return azcli.SnapshotsClient().CreateOrUpdateThenPoll(ctx, id, snapshot) + pollingContext, cancel := context.WithTimeout(ctx, azcli.PollingDelay()) + defer cancel() + return azcli.SnapshotsClient().CreateOrUpdateThenPoll(pollingContext, id, snapshot) } func (s *StepCreateSnapshotset) Cleanup(state multistep.StateBag) { @@ -103,7 +105,9 @@ func (s *StepCreateSnapshotset) Cleanup(state multistep.StateBag) { snapshotID := snapshots.NewSnapshotID(azcli.SubscriptionID(), resource.ResourceGroup, resource.ResourceName.String()) ui.Say(fmt.Sprintf("Removing any active SAS for snapshot %q", resource)) { - err := azcli.SnapshotsClient().RevokeAccessThenPoll(context.TODO(), snapshotID) + pollingContext, cancel := context.WithTimeout(context.TODO(), azcli.PollingDelay()) + defer cancel() + err := azcli.SnapshotsClient().RevokeAccessThenPoll(pollingContext, snapshotID) if err != nil { log.Printf("StepCreateSnapshotset.Cleanup: error: %+v", err) ui.Error(fmt.Sprintf("error deleting snapshot %q: %v.", resource, err)) @@ -112,7 +116,9 @@ func (s *StepCreateSnapshotset) Cleanup(state multistep.StateBag) { ui.Say(fmt.Sprintf("Deleting snapshot %q", resource)) { - err := azcli.SnapshotsClient().DeleteThenPoll(context.TODO(), snapshotID) + pollingContext, cancel := context.WithTimeout(context.TODO(), azcli.PollingDelay()) + defer cancel() + err := azcli.SnapshotsClient().DeleteThenPoll(pollingContext, snapshotID) 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/common/artifact.go b/builder/azure/common/artifact.go index 2d17822d..12981919 100644 --- a/builder/azure/common/artifact.go +++ b/builder/azure/common/artifact.go @@ -85,7 +85,9 @@ func (a *Artifact) Destroy() error { switch restype { case "microsoft.compute/images": imageID := images.NewImageID(a.AzureClientSet.SubscriptionID(), id.ResourceGroup, id.ResourceName.String()) - err := a.AzureClientSet.ImagesClient().DeleteThenPoll(ctx, imageID) + pollingContext, cancel := context.WithTimeout(ctx, a.AzureClientSet.PollingDelay()) + defer cancel() + err := a.AzureClientSet.ImagesClient().DeleteThenPoll(pollingContext, imageID) if err != nil { errs = append(errs, fmt.Errorf("Unable to initiate deletion of resource (%s): %v", resource, err)) } diff --git a/builder/azure/common/client/azure_authorizer.go b/builder/azure/common/client/azure_authorizer.go index ce64db9c..337e24b3 100644 --- a/builder/azure/common/client/azure_authorizer.go +++ b/builder/azure/common/client/azure_authorizer.go @@ -13,7 +13,7 @@ import ( "github.com/hashicorp/go-azure-sdk/sdk/environments" ) -type NewSDKAuthOptions struct { +type AzureAuthOptions struct { AuthType string ClientID string ClientSecret string @@ -23,7 +23,7 @@ type NewSDKAuthOptions struct { SubscriptionID string } -func BuildResourceManagerAuthorizer(ctx context.Context, authOpts NewSDKAuthOptions, env environments.Environment) (auth.Authorizer, error) { +func BuildResourceManagerAuthorizer(ctx context.Context, authOpts AzureAuthOptions, env environments.Environment) (auth.Authorizer, error) { authorizer, err := buildAuthorizer(ctx, authOpts, env, env.ResourceManager) if err != nil { return nil, fmt.Errorf("building Resource Manager authorizer from credentials: %+v", err) @@ -31,7 +31,7 @@ func BuildResourceManagerAuthorizer(ctx context.Context, authOpts NewSDKAuthOpti return authorizer, nil } -func BuildStorageAuthorizer(ctx context.Context, authOpts NewSDKAuthOptions, env environments.Environment) (auth.Authorizer, error) { +func BuildStorageAuthorizer(ctx context.Context, authOpts AzureAuthOptions, env environments.Environment) (auth.Authorizer, error) { authorizer, err := buildAuthorizer(ctx, authOpts, env, env.Storage) if err != nil { return nil, fmt.Errorf("building Storage authorizer from credentials: %+v", err) @@ -39,7 +39,7 @@ func BuildStorageAuthorizer(ctx context.Context, authOpts NewSDKAuthOptions, env return authorizer, nil } -func buildAuthorizer(ctx context.Context, authOpts NewSDKAuthOptions, env environments.Environment, api environments.Api) (auth.Authorizer, error) { +func buildAuthorizer(ctx context.Context, authOpts AzureAuthOptions, env environments.Environment, api environments.Api) (auth.Authorizer, error) { var authConfig auth.Credentials switch authOpts.AuthType { case AuthTypeAzureCLI: @@ -97,5 +97,8 @@ func GetObjectIdFromToken(token string) (string, error) { if err != nil { return "", err } + if claims["oid"] == nil { + return "", fmt.Errorf("unable to parse ObjectID from Azure") + } return claims["oid"].(string), nil } diff --git a/builder/azure/common/client/azure_client_set.go b/builder/azure/common/client/azure_client_set.go index 5bd10529..a1c2072c 100644 --- a/builder/azure/common/client/azure_client_set.go +++ b/builder/azure/common/client/azure_client_set.go @@ -42,6 +42,8 @@ type AzureClientSet interface { // SubscriptionID returns the subscription ID that this client set was created for SubscriptionID() string + + PollingDelay() time.Duration } var _ AzureClientSet = &azureClientSet{} @@ -50,7 +52,7 @@ type azureClientSet struct { sender autorest.Sender authorizer auth.Authorizer subscriptionID string - PollingDelay time.Duration + pollingDelay time.Duration ResourceManagerEndpoint string } @@ -60,7 +62,7 @@ func New(c Config, say func(string)) (AzureClientSet, error) { func new(c Config, say func(string)) (*azureClientSet, error) { // Pass in relevant auth information for hashicorp/go-azure-sdk - authOptions := NewSDKAuthOptions{ + authOptions := AzureAuthOptions{ AuthType: c.AuthType(), ClientID: c.ClientID, ClientSecret: c.ClientSecret, @@ -79,7 +81,7 @@ func new(c Config, say func(string)) (*azureClientSet, error) { authorizer: authorizer, subscriptionID: c.SubscriptionID, sender: http.DefaultClient, - PollingDelay: time.Second, + pollingDelay: time.Second, ResourceManagerEndpoint: *resourceManagerEndpoint, }, nil } @@ -88,6 +90,10 @@ func (s azureClientSet) SubscriptionID() string { return s.subscriptionID } +func (s azureClientSet) PollingDelay() time.Duration { + return s.pollingDelay +} + func (s azureClientSet) configureTrack1Client(c *autorest.Client) { err := c.AddToUserAgent(useragent.String(version.AzurePluginVersion.FormattedVersion())) if err != nil { @@ -107,49 +113,49 @@ func (s azureClientSet) MetadataClient() MetadataClientAPI { func (s azureClientSet) DisksClient() disks.DisksClient { c := disks.NewDisksClientWithBaseURI(s.ResourceManagerEndpoint) s.configureTrack1Client(&c.Client) - c.Client.PollingDelay = s.PollingDelay + c.Client.PollingDelay = s.pollingDelay return c } func (s azureClientSet) SnapshotsClient() snapshots.SnapshotsClient { c := snapshots.NewSnapshotsClientWithBaseURI(s.ResourceManagerEndpoint) s.configureTrack1Client(&c.Client) - c.Client.PollingDelay = s.PollingDelay + c.Client.PollingDelay = s.pollingDelay return c } func (s azureClientSet) ImagesClient() images.ImagesClient { c := images.NewImagesClientWithBaseURI(s.ResourceManagerEndpoint) s.configureTrack1Client(&c.Client) - c.Client.PollingDelay = s.PollingDelay + c.Client.PollingDelay = s.pollingDelay return c } func (s azureClientSet) VirtualMachinesClient() virtualmachines.VirtualMachinesClient { c := virtualmachines.NewVirtualMachinesClientWithBaseURI(s.ResourceManagerEndpoint) s.configureTrack1Client(&c.Client) - c.Client.PollingDelay = s.PollingDelay + c.Client.PollingDelay = s.pollingDelay return c } func (s azureClientSet) VirtualMachineImagesClient() virtualmachineimages.VirtualMachineImagesClient { c := virtualmachineimages.NewVirtualMachineImagesClientWithBaseURI(s.ResourceManagerEndpoint) s.configureTrack1Client(&c.Client) - c.Client.PollingDelay = s.PollingDelay + c.Client.PollingDelay = s.pollingDelay return c } func (s azureClientSet) GalleryImagesClient() galleryimages.GalleryImagesClient { c := galleryimages.NewGalleryImagesClientWithBaseURI(s.ResourceManagerEndpoint) s.configureTrack1Client(&c.Client) - c.Client.PollingDelay = s.PollingDelay + c.Client.PollingDelay = s.pollingDelay return c } func (s azureClientSet) GalleryImageVersionsClient() galleryimageversions.GalleryImageVersionsClient { c := galleryimageversions.NewGalleryImageVersionsClientWithBaseURI(s.ResourceManagerEndpoint) s.configureTrack1Client(&c.Client) - c.Client.PollingDelay = s.PollingDelay + c.Client.PollingDelay = s.pollingDelay return c } diff --git a/builder/azure/common/client/azure_client_set_mock.go b/builder/azure/common/client/azure_client_set_mock.go index b03ba90e..dca903c0 100644 --- a/builder/azure/common/client/azure_client_set_mock.go +++ b/builder/azure/common/client/azure_client_set_mock.go @@ -4,6 +4,8 @@ package client import ( + "time" + "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-01/images" "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-01/virtualmachineimages" "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-01/virtualmachines" @@ -26,6 +28,7 @@ type AzureClientSetMock struct { GalleryImageVersionsClientMock galleryimageversions.GalleryImageVersionsClient MetadataClientMock MetadataClientAPI SubscriptionIDMock string + PollingDelayMock time.Duration } // DisksClient returns a DisksClient @@ -72,3 +75,7 @@ func (m *AzureClientSetMock) MetadataClient() MetadataClientAPI { func (m *AzureClientSetMock) SubscriptionID() string { return m.SubscriptionIDMock } + +func (m *AzureClientSetMock) PollingDelay() time.Duration { + return m.PollingDelayMock +} diff --git a/builder/azure/common/client/config.go b/builder/azure/common/client/config.go index 323e207d..6142816a 100644 --- a/builder/azure/common/client/config.go +++ b/builder/azure/common/client/config.go @@ -124,8 +124,8 @@ func (c *Config) setCloudEnvironment() error { c.cloudEnvironment = env if err != nil { // fall back to old method of normalizing and looking up cloud names. - log.Printf(fmt.Sprintf("Error looking up environment using metadata host: %s. \n"+ - "Falling back to hardcoded mechanism...", err.Error())) + log.Printf("Error looking up environment using metadata host: %s. \n"+ + "Falling back to hardcoded mechanism...", err.Error()) lookup := map[string]string{ "CHINA": "china", "CHINACLOUD": "china", diff --git a/builder/azure/common/constants/stateBag.go b/builder/azure/common/constants/stateBag.go index 20125ae7..a0fbffb4 100644 --- a/builder/azure/common/constants/stateBag.go +++ b/builder/azure/common/constants/stateBag.go @@ -34,14 +34,8 @@ const ( ArmResourceGroupName string = "arm.ResourceGroupName" ArmIsResourceGroupCreated string = "arm.IsResourceGroupCreated" ArmDoubleResourceGroupNameSet string = "arm.DoubleResourceGroupNameSet" - // TODO Replace ArmTags with ArmNewSDKTags - // Temporary object, new SDK expects *map[string]string instead of map [string]*string - ArmNewSDKTags string = "arm.NewSDKTags" ArmStorageAccountName string = "arm.StorageAccountName" ArmTags string = "arm.Tags" - // TODO Replace ArmVirtualMachineCaptureParameters with ArmNewVirtualMachineCaptureParameters - // Temporary object, this code is shared by all three builders so we need a new object for the diff type of capture parameters - ArmNewVirtualMachineCaptureParameters string = "arm.NewVirtualMachineCaptureParameters" ArmVirtualMachineCaptureParameters string = "arm.VirtualMachineCaptureParameters" ArmIsExistingResourceGroup string = "arm.IsExistingResourceGroup" ArmIsExistingKeyVault string = "arm.IsExistingKeyVault" diff --git a/builder/azure/dtl/azure_client.go b/builder/azure/dtl/azure_client.go index d9fd844c..b1bb0073 100644 --- a/builder/azure/dtl/azure_client.go +++ b/builder/azure/dtl/azure_client.go @@ -20,6 +20,7 @@ import ( "github.com/hashicorp/go-azure-sdk/resource-manager/keyvault/2023-02-01/vaults" networks "github.com/hashicorp/go-azure-sdk/resource-manager/network/2022-09-01" authWrapper "github.com/hashicorp/go-azure-sdk/sdk/auth/autorest" + "github.com/hashicorp/go-azure-sdk/sdk/client" "github.com/hashicorp/go-azure-sdk/sdk/client/resourcemanager" "github.com/hashicorp/go-azure-sdk/sdk/environments" azcommon "github.com/hashicorp/packer-plugin-azure/builder/azure/common/client" @@ -41,6 +42,10 @@ type AzureClient struct { galleryimageversions.GalleryImageVersionsClient galleryimages.GalleryImagesClient DtlMetaClient dtl.Client + + PollingDuration time.Duration + CustomImageCaptureTimeout time.Duration + SharedGalleryTimeout time.Duration } func errorCapture(client *AzureClient) autorest.RespondDecorator { @@ -59,7 +64,19 @@ func errorCapture(client *AzureClient) autorest.RespondDecorator { } } -// TODO Do we need a track 2 version of this method? +func errorCaptureTrack2(client *AzureClient) client.ResponseMiddleware { + return func(req *http.Request, resp *http.Response) (*http.Response, error) { + body, bodyString := handleBody(resp.Body, math.MaxInt64) + resp.Body = body + + errorResponse := newAzureErrorResponse(bodyString) + if errorResponse != nil { + client.LastError = *errorResponse + } + return resp, nil + } +} + func byConcatDecorators(decorators ...autorest.RespondDecorator) autorest.RespondDecorator { return func(r autorest.Responder) autorest.Responder { return autorest.DecorateResponder(r, decorators...) @@ -69,18 +86,24 @@ func byConcatDecorators(decorators ...autorest.RespondDecorator) autorest.Respon // Returns an Azure Client used for the Azure Resource Manager // Also returns the Azure object ID for the authentication method used in the build func NewAzureClient(ctx context.Context, subscriptionID string, - cloud *environments.Environment, SharedGalleryTimeout time.Duration, CustomImageCaptureTimeout time.Duration, PollingDuration time.Duration, newSdkAuthOptions azcommon.NewSDKAuthOptions) (*AzureClient, *string, error) { + cloud *environments.Environment, SharedGalleryTimeout time.Duration, CustomImageCaptureTimeout time.Duration, PollingDuration time.Duration, authOptions azcommon.AzureAuthOptions) (*AzureClient, *string, error) { var azureClient = &AzureClient{} maxlen := getInspectorMaxLength() + trackTwoResponseMiddleware := []client.ResponseMiddleware{byInspectingTrack2(maxlen), errorCaptureTrack2(azureClient)} + trackTwoRequestMiddleware := []client.RequestMiddleware{withInspectionTrack2(maxlen)} + + azureClient.CustomImageCaptureTimeout = CustomImageCaptureTimeout + azureClient.PollingDuration = PollingDuration + azureClient.SharedGalleryTimeout = SharedGalleryTimeout + if cloud == nil || cloud.ResourceManager == nil { - // TODO Throw error message that helps users solve this problem return nil, nil, fmt.Errorf("Azure Environment not configured correctly") } resourceManagerEndpoint, _ := cloud.ResourceManager.Endpoint() - resourceManagerAuthorizer, err := azcommon.BuildResourceManagerAuthorizer(ctx, newSdkAuthOptions, *cloud) + resourceManagerAuthorizer, err := azcommon.BuildResourceManagerAuthorizer(ctx, authOptions, *cloud) if err != nil { return nil, nil, err } @@ -113,10 +136,11 @@ func NewAzureClient(ctx context.Context, subscriptionID string, azureClient.ImagesClient.Client.UserAgent = fmt.Sprintf("%s %s", useragent.String(version.AzurePluginVersion.FormattedVersion()), azureClient.ImagesClient.Client.UserAgent) azureClient.ImagesClient.Client.PollingDuration = PollingDuration - // TODO Request/Response inpectors for Track 2 networkMetaClient, err := networks.NewClientWithBaseURI(cloud.ResourceManager, func(c *resourcemanager.Client) { c.Client.Authorizer = resourceManagerAuthorizer c.Client.UserAgent = "some-user-agent" + c.Client.RequestMiddlewares = &trackTwoRequestMiddleware + c.Client.ResponseMiddlewares = &trackTwoResponseMiddleware }) if err != nil { diff --git a/builder/azure/dtl/builder.go b/builder/azure/dtl/builder.go index b7d8453b..25218985 100644 --- a/builder/azure/dtl/builder.go +++ b/builder/azure/dtl/builder.go @@ -75,7 +75,7 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) b.stateBag.Put(constants.Ui, ui) // Pass in relevant auth information for hashicorp/go-azure-sdk - authOptions := commonclient.NewSDKAuthOptions{ + authOptions := commonclient.AzureAuthOptions{ AuthType: b.config.ClientConfig.AuthType(), ClientID: b.config.ClientConfig.ClientID, ClientSecret: b.config.ClientConfig.ClientSecret, @@ -119,8 +119,10 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) if err == nil { if b.config.PackerForce { + pollingContext, cancel := context.WithTimeout(ctx, azureClient.CustomImageCaptureTimeout) + defer cancel() ui.Say(fmt.Sprintf("the managed image named %s already exists, but deleting it due to -force flag", b.config.ManagedImageName)) - err := azureClient.DtlMetaClient.CustomImages.DeleteThenPoll(ctx, customImageResourceId) + err := azureClient.DtlMetaClient.CustomImages.DeleteThenPoll(pollingContext, customImageResourceId) if err != nil { return nil, fmt.Errorf("failed to delete the managed image named %s : %s", b.config.ManagedImageName, azureClient.LastError.Error()) } @@ -165,7 +167,7 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) continue } } - if foundMandatoryReplicationRegion == false { + if !foundMandatoryReplicationRegion { b.config.SharedGalleryDestination.SigDestinationReplicationRegions = append(normalizedReplicationRegions, managedImageLocation) } b.stateBag.Put(constants.ArmManagedImageSharedGalleryReplicationRegions, b.config.SharedGalleryDestination.SigDestinationReplicationRegions) @@ -300,8 +302,6 @@ func (b *Builder) writeSSHPrivateKey(ui packersdk.Ui, debugKeyPath string) { 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.ArmNewSDKTags, b.config.AzureTags) stateBag.Put(constants.ArmTags, b.config.AzureTags) stateBag.Put(constants.ArmComputeName, b.config.tmpComputeName) stateBag.Put(constants.ArmDeploymentName, b.config.tmpDeploymentName) diff --git a/builder/azure/dtl/inspector.go b/builder/azure/dtl/inspector.go index 5dbe3b67..d0aeb42e 100644 --- a/builder/azure/dtl/inspector.go +++ b/builder/azure/dtl/inspector.go @@ -13,6 +13,7 @@ import ( "github.com/Azure/go-autorest/autorest" "github.com/Azure/go-autorest/autorest/azure" + "github.com/hashicorp/go-azure-sdk/sdk/client" "github.com/hashicorp/packer-plugin-azure/builder/azure/common/logutil" ) @@ -72,3 +73,34 @@ func byInspecting(maxlen int64) autorest.RespondDecorator { }) } } + +func withInspectionTrack2(maxlen int64) client.RequestMiddleware { + return 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 r, nil + } +} + +func byInspectingTrack2(maxlen int64) client.ResponseMiddleware { + return func(req *http.Request, resp *http.Response) (*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 resp, nil + } +} \ No newline at end of file diff --git a/builder/azure/dtl/step_capture_image.go b/builder/azure/dtl/step_capture_image.go index 49c06938..1a1e1171 100644 --- a/builder/azure/dtl/step_capture_image.go +++ b/builder/azure/dtl/step_capture_image.go @@ -82,7 +82,9 @@ func (s *StepCaptureImage) captureImageFromVM(ctx context.Context) error { } customImageId := customimages.NewCustomImageID(s.config.ClientConfig.SubscriptionID, s.config.LabResourceGroupName, s.config.LabName, s.config.ManagedImageName) - err := s.client.DtlMetaClient.CustomImages.CreateOrUpdateThenPoll(ctx, customImageId, *customImage) + pollingContext, cancel := context.WithTimeout(ctx, s.client.CustomImageCaptureTimeout) + defer cancel() + err := s.client.DtlMetaClient.CustomImages.CreateOrUpdateThenPoll(pollingContext, customImageId, *customImage) if err != nil { s.say("Error from Capture Image") s.say(s.client.LastError.Error()) diff --git a/builder/azure/dtl/step_delete_virtual_machine.go b/builder/azure/dtl/step_delete_virtual_machine.go index 148b2f8e..7662bab6 100644 --- a/builder/azure/dtl/step_delete_virtual_machine.go +++ b/builder/azure/dtl/step_delete_virtual_machine.go @@ -34,8 +34,10 @@ func NewStepDeleteVirtualMachine(client *AzureClient, ui packersdk.Ui, config *C } func (s *StepDeleteVirtualMachine) deleteVirtualMachine(ctx context.Context, subscriptionId string, labName string, resourceGroupName string, vmName string) error { + pollingContext, cancel := context.WithTimeout(ctx, s.client.PollingDuration) + defer cancel() vmId := dtlvirtualmachines.NewVirtualMachineID(subscriptionId, resourceGroupName, labName, vmName) - err := s.client.DtlMetaClient.VirtualMachines.DeleteThenPoll(ctx, vmId) + err := s.client.DtlMetaClient.VirtualMachines.DeleteThenPoll(pollingContext, vmId) if err != nil { s.say("Error from delete VM") s.say(s.client.LastError.Error()) diff --git a/builder/azure/dtl/step_deploy_template.go b/builder/azure/dtl/step_deploy_template.go index f6e4e8fe..8f4c342a 100644 --- a/builder/azure/dtl/step_deploy_template.go +++ b/builder/azure/dtl/step_deploy_template.go @@ -71,7 +71,9 @@ func (s *StepDeployTemplate) deployTemplate(ctx context.Context, resourceGroupNa return err } - err = s.client.DtlMetaClient.Labs.CreateEnvironmentThenPoll(ctx, labId, *labMachine) + pollingContext, cancel := context.WithTimeout(ctx, s.client.PollingDuration) + defer cancel() + err = s.client.DtlMetaClient.Labs.CreateEnvironmentThenPoll(pollingContext, labId, *labMachine) if err != nil { s.say(s.client.LastError.Error()) return err @@ -93,7 +95,6 @@ func (s *StepDeployTemplate) deployTemplate(ctx context.Context, resourceGroupNa s.say(s.client.LastError.Error()) return err } - // TODO This operation seems kinda off, but I don't wanna spend time digging into it right now s.config.tmpFQDN = *(*resp.Model.Properties.IPConfigurations)[0].Properties.PrivateIPAddress } else { s.config.tmpFQDN = *vm.Model.Properties.Fqdn @@ -133,11 +134,13 @@ func (s *StepDeployTemplate) deployTemplate(ctx context.Context, resourceGroupNa // But a retry backoff is much more preferable to an infinite loop retryConfig := retry.Config{ - Tries: 5, + Tries: 10, RetryDelay: (&retry.Backoff{InitialBackoff: 5 * time.Second, MaxBackoff: 60 * time.Second, Multiplier: 1.5}).Linear, } err = retryConfig.Run(ctx, func(ctx context.Context) error { - err := s.client.DtlMetaClient.VirtualMachines.ApplyArtifactsThenPoll(ctx, vmResourceId, dtlArtifactsRequest) + pollingContext, cancel := context.WithTimeout(ctx, s.client.PollingDuration) + defer cancel() + err := s.client.DtlMetaClient.VirtualMachines.ApplyArtifactsThenPoll(pollingContext, vmResourceId, dtlArtifactsRequest) if err != nil { s.say("WinRM artifact deployment failed, retrying") } @@ -171,7 +174,4 @@ func (s *StepDeployTemplate) Run(ctx context.Context, state multistep.StateBag) s.error, state) } -func (s *StepDeployTemplate) Cleanup(state multistep.StateBag) { - // TODO are there any resources created in DTL builds we should tear down? - // There was teardown code from the ARM builder copy pasted in but it was never called -} +func (s *StepDeployTemplate) Cleanup(state multistep.StateBag) {} diff --git a/builder/azure/dtl/step_power_off_compute.go b/builder/azure/dtl/step_power_off_compute.go index 4ec364ba..dabe3e7e 100644 --- a/builder/azure/dtl/step_power_off_compute.go +++ b/builder/azure/dtl/step_power_off_compute.go @@ -35,8 +35,10 @@ func NewStepPowerOffCompute(client *AzureClient, ui packersdk.Ui, config *Config } func (s *StepPowerOffCompute) powerOffCompute(ctx context.Context, resourceGroupName string, labName, computeName string) error { + pollingContext, cancel := context.WithTimeout(ctx, s.client.PollingDuration) + defer cancel() vmResourceId := virtualmachines.NewVirtualMachineID(s.config.ClientConfig.SubscriptionID, s.config.tmpResourceGroupName, labName, computeName) - err := s.client.DtlMetaClient.VirtualMachines.StopThenPoll(ctx, vmResourceId) + err := s.client.DtlMetaClient.VirtualMachines.StopThenPoll(pollingContext, vmResourceId) if err != nil { s.say(s.client.LastError.Error()) } diff --git a/builder/azure/dtl/step_publish_to_shared_image_gallery.go b/builder/azure/dtl/step_publish_to_shared_image_gallery.go index 1ad778a9..708ccf6b 100644 --- a/builder/azure/dtl/step_publish_to_shared_image_gallery.go +++ b/builder/azure/dtl/step_publish_to_shared_image_gallery.go @@ -62,8 +62,10 @@ func (s *StepPublishToSharedImageGallery) publishToSig(ctx context.Context, subs }, } + pollingContext, cancel := context.WithTimeout(ctx, s.client.SharedGalleryTimeout) + defer cancel() galleryImageVersionId := galleryimageversions.NewImageVersionID(subscriptionID, sigDestinationResourceGroup, sigDestinationGalleryName, sigDestinationImageName, sigDestinationImageVersion) - err := s.client.GalleryImageVersionsClient.CreateOrUpdateThenPoll(ctx, galleryImageVersionId, galleryImageVersion) + err := s.client.GalleryImageVersionsClient.CreateOrUpdateThenPoll(pollingContext, galleryImageVersionId, galleryImageVersion) if err != nil { s.say(s.client.LastError.Error()) @@ -92,7 +94,7 @@ func (s *StepPublishToSharedImageGallery) Run(ctx context.Context, stateBag mult 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.ArmNewSDKTags).(map[string]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) diff --git a/builder/azure/dtl/template_factory.go b/builder/azure/dtl/template_factory.go index 872e690e..ad30aea7 100644 --- a/builder/azure/dtl/template_factory.go +++ b/builder/azure/dtl/template_factory.go @@ -103,8 +103,7 @@ func GetVirtualMachineDeployment(config *Config) (*labs.LabVirtualMachineCreatio labMachine := &labs.LabVirtualMachineCreationParameter{ Name: &config.tmpComputeName, Location: &config.Location, - // TODO - //Tags: config.AzureTags, + Tags: &config.AzureTags, Properties: labMachineProps, } diff --git a/go.mod b/go.mod index e2b8b2f4..cdb07410 100644 --- a/go.mod +++ b/go.mod @@ -21,6 +21,7 @@ require ( ) require ( + github.com/Azure/go-autorest/autorest/azure/cli v0.4.6 github.com/hashicorp/go-azure-sdk v0.20230523.1140858 github.com/tombuildsstuff/giovanni v0.20.0 ) @@ -31,7 +32,6 @@ require ( cloud.google.com/go/iam v0.6.0 // indirect cloud.google.com/go/storage v1.27.0 // indirect github.com/Azure/go-autorest v14.2.0+incompatible // indirect - github.com/Azure/go-autorest/autorest/azure/cli v0.4.6 // indirect github.com/Azure/go-autorest/autorest/validation v0.3.1 // indirect github.com/Azure/go-autorest/logger v0.2.1 // indirect github.com/Azure/go-autorest/tracing v0.6.0 // indirect diff --git a/go.sum b/go.sum index bdc171cd..8aeee1d8 100644 --- a/go.sum +++ b/go.sum @@ -99,8 +99,6 @@ github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6D github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= -github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/containerd/cgroups v0.0.0-20190919134610-bf292b21730f/go.mod h1:OApqhQ4XNSNC13gXIwDjhOQxjWa/NxkwZXJ1EvqT0ko= github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw= github.com/containerd/containerd v1.3.2/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= @@ -131,7 +129,6 @@ github.com/dylanmei/winrmtest v0.0.0-20170819153634-c2fbb09e6c08 h1:0bp6/GrNOrTD github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= @@ -209,7 +206,6 @@ github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian/v3 v3.2.1 h1:d8MncMlErDFTwQGBK1xhv026j9kqhvw1Qv9IbWT1VLQ= -github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= diff --git a/provisioner/azure-dtlartifact/provisioner.go b/provisioner/azure-dtlartifact/provisioner.go index 2529c075..53d0d132 100644 --- a/provisioner/azure-dtlartifact/provisioner.go +++ b/provisioner/azure-dtlartifact/provisioner.go @@ -126,7 +126,7 @@ func (p *Provisioner) Provision(ctx context.Context, ui packersdk.Ui, comm packe } // Pass in relevant auth information for hashicorp/go-azure-sdk - authOptions := client.NewSDKAuthOptions{ + authOptions := client.AzureAuthOptions{ AuthType: p.config.ClientConfig.AuthType(), ClientID: p.config.ClientConfig.ClientID, ClientSecret: p.config.ClientConfig.ClientSecret, @@ -184,8 +184,10 @@ func (p *Provisioner) Provision(ctx context.Context, ui packersdk.Ui, comm packe ui.Say("Applying artifact ") + pollingContext, cancel := context.WithTimeout(ctx, azureClient.PollingDuration) + defer cancel() vmResourceId := virtualmachines.NewVirtualMachineID(p.config.ClientConfig.SubscriptionID, p.config.ResourceGroupName, p.config.LabName, p.config.VMName) - err = azureClient.DtlMetaClient.VirtualMachines.ApplyArtifactsThenPoll(ctx, vmResourceId, dtlApplyArifactRequest) + err = azureClient.DtlMetaClient.VirtualMachines.ApplyArtifactsThenPoll(pollingContext, vmResourceId, dtlApplyArifactRequest) if err != nil { ui.Say(fmt.Sprintf("Error Applying artifact: %s", err)) From 78b7d4b15620194f68f72dd41b81233f82ec8f35 Mon Sep 17 00:00:00 2001 From: Jenna Goldstrich Date: Wed, 2 Aug 2023 18:35:46 -0700 Subject: [PATCH 08/15] Pass in expiry time when making a new Key Vault as well, lint [chroot] PollingDelay != Polling Duration >_> --- builder/azure/arm/azure_client.go | 16 ++-- builder/azure/arm/config.go | 2 +- builder/azure/arm/config.hcl2spec.go | 2 + builder/azure/arm/config_test.go | 10 +-- builder/azure/arm/inspector.go | 44 +++++------ builder/azure/arm/step_deploy_template.go | 6 +- builder/azure/arm/template_factory.go | 18 ++++- ...est.TestKeyVaultDeployment04.approved.json | 74 +++++++++++++++++++ builder/azure/arm/template_factory_test.go | 26 ++++++- builder/azure/chroot/step_create_image.go | 2 +- .../azure/chroot/step_create_new_diskset.go | 4 +- .../step_create_shared_image_version.go | 2 +- .../azure/chroot/step_create_snapshotset.go | 6 +- builder/azure/common/artifact.go | 2 +- .../azure/common/client/azure_client_set.go | 26 ++++--- .../common/client/azure_client_set_mock.go | 6 +- builder/azure/common/constants/stateBag.go | 32 ++++---- builder/azure/common/template/template.go | 5 ++ .../azure/common/template/template_builder.go | 12 +++ builder/azure/dtl/azure_client.go | 19 +++-- builder/azure/dtl/inspector.go | 44 +++++------ builder/azure/dtl/template_factory.go | 6 +- .../builder/azure/arm/Config-not-required.mdx | 4 + 23 files changed, 248 insertions(+), 120 deletions(-) create mode 100644 builder/azure/arm/template_factory_test.TestKeyVaultDeployment04.approved.json diff --git a/builder/azure/arm/azure_client.go b/builder/azure/arm/azure_client.go index 008d329f..766cc9e3 100644 --- a/builder/azure/arm/azure_client.go +++ b/builder/azure/arm/azure_client.go @@ -59,7 +59,7 @@ type AzureClient struct { InspectorMaxLength int LastError azureErrorResponse - PollingDuration time.Duration + PollingDuration time.Duration SharedGalleryTimeout time.Duration } @@ -82,13 +82,13 @@ func errorCapture(client *AzureClient) autorest.RespondDecorator { func errorCaptureTrack2(client *AzureClient) client.ResponseMiddleware { return func(req *http.Request, resp *http.Response) (*http.Response, error) { body, bodyString := handleBody(resp.Body, math.MaxInt64) - resp.Body = body + resp.Body = body - errorResponse := newAzureErrorResponse(bodyString) - if errorResponse != nil { - client.LastError = *errorResponse - } - return resp, nil + errorResponse := newAzureErrorResponse(bodyString) + if errorResponse != nil { + client.LastError = *errorResponse + } + return resp, nil } } @@ -114,7 +114,7 @@ func NewAzureClient(ctx context.Context, isVHDBuild bool, cloud *environments.En if err != nil { return nil, nil, err } - + trackTwoResponseMiddleware := []client.ResponseMiddleware{byInspectingTrack2(maxlen), errorCaptureTrack2(azureClient)} trackTwoRequestMiddleware := []client.RequestMiddleware{withInspectionTrack2(maxlen)} diff --git a/builder/azure/arm/config.go b/builder/azure/arm/config.go index aa3f226c..3445abca 100644 --- a/builder/azure/arm/config.go +++ b/builder/azure/arm/config.go @@ -490,11 +490,11 @@ type Config struct { // 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"` + // 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. - OSType string `mapstructure:"os_type" required:"false"` // A time duration with which to set the WinRM certificate to expire diff --git a/builder/azure/arm/config.hcl2spec.go b/builder/azure/arm/config.hcl2spec.go index 40376899..aaf30974 100644 --- a/builder/azure/arm/config.hcl2spec.go +++ b/builder/azure/arm/config.hcl2spec.go @@ -81,6 +81,7 @@ type FlatConfig struct { 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"` + WinrmExpirationTime *string `mapstructure:"winrm_expiration_time" required:"false" cty:"winrm_expiration_time" hcl:"winrm_expiration_time"` 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"` @@ -225,6 +226,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "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}, + "winrm_expiration_time": &hcldec.AttrSpec{Name: "winrm_expiration_time", 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}, diff --git a/builder/azure/arm/config_test.go b/builder/azure/arm/config_test.go index 1662955d..d8bbd796 100644 --- a/builder/azure/arm/config_test.go +++ b/builder/azure/arm/config_test.go @@ -2094,11 +2094,11 @@ func TestConfigShouldAllowSharedImageGalleryOptions(t *testing.T) { func TestConfigShouldRejectSharedImageGalleryDestinationInvalidVersion(t *testing.T) { config := map[string]interface{}{ - "location": "ignore", - "subscription_id": "ignore", - "os_type": "linux", - "image_sku": "ignore", - "image_offer": "ignore", + "location": "ignore", + "subscription_id": "ignore", + "os_type": "linux", + "image_sku": "ignore", + "image_offer": "ignore", "image_publisher": "ignore", "shared_image_gallery_destination": map[string]string{ "resource_group": "ignore", diff --git a/builder/azure/arm/inspector.go b/builder/azure/arm/inspector.go index 6135ae97..0020ae85 100644 --- a/builder/azure/arm/inspector.go +++ b/builder/azure/arm/inspector.go @@ -57,16 +57,16 @@ func withInspection(maxlen int64) autorest.PrepareDecorator { } func withInspectionTrack2(maxlen int64) client.RequestMiddleware { - return 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 r, nil + return 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 r, nil } } @@ -90,17 +90,17 @@ func byInspecting(maxlen int64) autorest.RespondDecorator { func byInspectingTrack2(maxlen int64) client.ResponseMiddleware { return func(req *http.Request, resp *http.Response) (*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, - }) + 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 resp, nil + return resp, nil } -} \ No newline at end of file +} diff --git a/builder/azure/arm/step_deploy_template.go b/builder/azure/arm/step_deploy_template.go index 4f4125f7..2e9d289b 100644 --- a/builder/azure/arm/step_deploy_template.go +++ b/builder/azure/arm/step_deploy_template.go @@ -217,10 +217,10 @@ func (s *StepDeployTemplate) getImageDetails(ctx context.Context, subscriptionId } func deleteResource(ctx context.Context, client *AzureClient, subscriptionId string, resourceType string, resourceName string, resourceGroupName string) error { - + pollingContext, cancel := context.WithTimeout(ctx, client.PollingDuration) defer cancel() - + switch resourceType { case "Microsoft.Compute/virtualMachines": vmID := virtualmachines.NewVirtualMachineID(subscriptionId, resourceGroupName, resourceName) @@ -251,7 +251,7 @@ func deleteResource(ctx context.Context, client *AzureClient, subscriptionId str return nil } -// TODO Let's split this into two seperate methods +// TODO Let's split this into two seperate methods // deleteVHD and deleteManagedDisk, and then just check in Cleanup which function to call func (s *StepDeployTemplate) deleteImage(ctx context.Context, imageName string, resourceGroupName string, isManagedDisk bool, subscriptionId string, storageAccountName string) error { // Managed disk diff --git a/builder/azure/arm/template_factory.go b/builder/azure/arm/template_factory.go index 54654c93..d1a4d6ad 100644 --- a/builder/azure/arm/template_factory.go +++ b/builder/azure/arm/template_factory.go @@ -8,6 +8,7 @@ import ( "encoding/json" "errors" "fmt" + "time" hashiVMSDK "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-01/virtualmachines" "github.com/hashicorp/go-azure-sdk/resource-manager/resources/2022-09-01/deployments" @@ -33,13 +34,18 @@ func GetCommunicatorSpecificKeyVaultDeployment(config *Config) (*deployments.Dep if err != nil { return nil, err } - return GetKeyVaultDeployment(config, secret) + return GetKeyVaultDeployment(config, secret, nil) } else { - return GetKeyVaultDeployment(config, config.winrmCertificate) + var exp *int64 + if config.WinrmExpirationTime != 0 { + unixSeconds := time.Now().Add(config.WinrmExpirationTime).Unix() + exp = &unixSeconds + } + return GetKeyVaultDeployment(config, config.winrmCertificate, exp) } } -func GetKeyVaultDeployment(config *Config, secretValue string) (*deployments.Deployment, error) { +func GetKeyVaultDeployment(config *Config, secretValue string, exp *int64) (*deployments.Deployment, error) { params := &template.TemplateParameters{ KeyVaultName: &template.TemplateParameter{Value: config.tmpKeyVaultName}, KeyVaultSKU: &template.TemplateParameter{Value: config.BuildKeyVaultSKU}, @@ -51,6 +57,12 @@ func GetKeyVaultDeployment(config *Config, secretValue string) (*deployments.Dep builder, _ := template.NewTemplateBuilder(template.KeyVault) _ = builder.SetTags(&config.AzureTags) + if exp != nil { + err := builder.SetSecretExpiry(*exp) + if err != nil { + return nil, err + } + } doc, _ := builder.ToJSON() return createDeploymentParameters(*doc, params) } diff --git a/builder/azure/arm/template_factory_test.TestKeyVaultDeployment04.approved.json b/builder/azure/arm/template_factory_test.TestKeyVaultDeployment04.approved.json new file mode 100644 index 00000000..e25d3b30 --- /dev/null +++ b/builder/azure/arm/template_factory_test.TestKeyVaultDeployment04.approved.json @@ -0,0 +1,74 @@ +{ + "$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": { + "attributes": { + "exp": 4102444800 + }, + "value": "[parameters('keyVaultSecretValue')]" + }, + "type": "secrets" + } + ], + "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.go b/builder/azure/arm/template_factory_test.go index f6652a59..bba89516 100644 --- a/builder/azure/arm/template_factory_test.go +++ b/builder/azure/arm/template_factory_test.go @@ -528,7 +528,7 @@ func TestKeyVaultDeployment00(t *testing.T) { if err != nil { t.Fatal(err) } - deployment, err := GetKeyVaultDeployment(&c, "secret") + deployment, err := GetKeyVaultDeployment(&c, "secret", nil) if err != nil { t.Fatal(err) } @@ -561,7 +561,7 @@ func TestKeyVaultDeployment01(t *testing.T) { if err != nil { t.Fatal(err) } - deployment, err := GetKeyVaultDeployment(&c, "secret") + deployment, err := GetKeyVaultDeployment(&c, "secret", nil) if err != nil { t.Fatal(err) } @@ -579,7 +579,7 @@ func TestKeyVaultDeployment02(t *testing.T) { if err != nil { t.Fatal(err) } - deployment, err := GetKeyVaultDeployment(&c, c.winrmCertificate) + deployment, err := GetKeyVaultDeployment(&c, c.winrmCertificate, nil) if err != nil { t.Fatal(err) } @@ -655,7 +655,25 @@ func TestKeyVaultDeployment03(t *testing.T) { if err != nil { t.Fatal(err) } - deployment, err := GetKeyVaultDeployment(&c, c.winrmCertificate) + deployment, err := GetKeyVaultDeployment(&c, c.winrmCertificate, nil) + if err != nil { + t.Fatal(err) + } + + approvaltests.VerifyJSONStruct(t, deployment.Properties.Template) +} + +// Ensure the KeyVault template is correct when tags are supplied. +func TestKeyVaultDeployment04(t *testing.T) { + + var c Config + _, err := c.Prepare(getArmBuilderConfigurationWithWindows(), getPackerConfiguration()) + if err != nil { + t.Fatal(err) + } + // January 1st 2100 + expiry := int64(4102444800) + deployment, err := GetKeyVaultDeployment(&c, c.winrmCertificate, &expiry) if err != nil { t.Fatal(err) } diff --git a/builder/azure/chroot/step_create_image.go b/builder/azure/chroot/step_create_image.go index 1763f19e..4be48a0b 100644 --- a/builder/azure/chroot/step_create_image.go +++ b/builder/azure/chroot/step_create_image.go @@ -119,7 +119,7 @@ func (s *StepCreateImage) Run(ctx context.Context, state multistep.StateBag) mul } func (s *StepCreateImage) createImage(ctx context.Context, client client.AzureClientSet, id images.ImageId, image images.Image) error { - pollingContext, cancel := context.WithTimeout(ctx, client.PollingDelay()) + pollingContext, cancel := context.WithTimeout(ctx, client.PollingDuration()) defer cancel() return client.ImagesClient().CreateOrUpdateThenPoll(pollingContext, id, image) } diff --git a/builder/azure/chroot/step_create_new_diskset.go b/builder/azure/chroot/step_create_new_diskset.go index e391116d..910b6ebc 100644 --- a/builder/azure/chroot/step_create_new_diskset.go +++ b/builder/azure/chroot/step_create_new_diskset.go @@ -258,8 +258,8 @@ func (s *StepCreateNewDiskset) Cleanup(state multistep.StateBag) { ui.Say(fmt.Sprintf("Deleting disk %q", d)) diskID := disks.NewDiskID(azcli.SubscriptionID(), d.ResourceGroup, d.ResourceName.String()) - pollingContext, cancel := context.WithTimeout(context.TODO(), azcli.PollingDelay()) - defer cancel() + pollingContext, cancel := context.WithTimeout(context.TODO(), azcli.PollingDuration()) + defer cancel() err = azcli.DisksClient().DeleteThenPoll(pollingContext, diskID) if err != nil { log.Printf("StepCreateNewDiskset.Cleanup: error: %+v", err) diff --git a/builder/azure/chroot/step_create_shared_image_version.go b/builder/azure/chroot/step_create_shared_image_version.go index f68f0fae..c0251770 100644 --- a/builder/azure/chroot/step_create_shared_image_version.go +++ b/builder/azure/chroot/step_create_shared_image_version.go @@ -116,7 +116,7 @@ func (s *StepCreateSharedImageVersion) Run(ctx context.Context, state multistep. } func (s *StepCreateSharedImageVersion) createImageVersion(ctx context.Context, azcli client.AzureClientSet, galleryImageVersionID galleryimageversions.ImageVersionId, imageVersion galleryimageversions.GalleryImageVersion) error { - pollingContext, cancel := context.WithTimeout(ctx, azcli.PollingDelay()) + pollingContext, cancel := context.WithTimeout(ctx, azcli.PollingDuration()) defer cancel() return azcli.GalleryImageVersionsClient().CreateOrUpdateThenPoll( pollingContext, diff --git a/builder/azure/chroot/step_create_snapshotset.go b/builder/azure/chroot/step_create_snapshotset.go index 7d4aeffc..2d18459a 100644 --- a/builder/azure/chroot/step_create_snapshotset.go +++ b/builder/azure/chroot/step_create_snapshotset.go @@ -90,7 +90,7 @@ func (s *StepCreateSnapshotset) Run(ctx context.Context, state multistep.StateBa } func (s *StepCreateSnapshotset) createSnapshot(ctx context.Context, azcli client.AzureClientSet, id snapshots.SnapshotId, snapshot snapshots.Snapshot) error { - pollingContext, cancel := context.WithTimeout(ctx, azcli.PollingDelay()) + pollingContext, cancel := context.WithTimeout(ctx, azcli.PollingDuration()) defer cancel() return azcli.SnapshotsClient().CreateOrUpdateThenPoll(pollingContext, id, snapshot) } @@ -105,7 +105,7 @@ func (s *StepCreateSnapshotset) Cleanup(state multistep.StateBag) { snapshotID := snapshots.NewSnapshotID(azcli.SubscriptionID(), resource.ResourceGroup, resource.ResourceName.String()) ui.Say(fmt.Sprintf("Removing any active SAS for snapshot %q", resource)) { - pollingContext, cancel := context.WithTimeout(context.TODO(), azcli.PollingDelay()) + pollingContext, cancel := context.WithTimeout(context.TODO(), azcli.PollingDuration()) defer cancel() err := azcli.SnapshotsClient().RevokeAccessThenPoll(pollingContext, snapshotID) if err != nil { @@ -116,7 +116,7 @@ func (s *StepCreateSnapshotset) Cleanup(state multistep.StateBag) { ui.Say(fmt.Sprintf("Deleting snapshot %q", resource)) { - pollingContext, cancel := context.WithTimeout(context.TODO(), azcli.PollingDelay()) + pollingContext, cancel := context.WithTimeout(context.TODO(), azcli.PollingDuration()) defer cancel() err := azcli.SnapshotsClient().DeleteThenPoll(pollingContext, snapshotID) if err != nil { diff --git a/builder/azure/common/artifact.go b/builder/azure/common/artifact.go index 12981919..c59683d4 100644 --- a/builder/azure/common/artifact.go +++ b/builder/azure/common/artifact.go @@ -85,7 +85,7 @@ func (a *Artifact) Destroy() error { switch restype { case "microsoft.compute/images": imageID := images.NewImageID(a.AzureClientSet.SubscriptionID(), id.ResourceGroup, id.ResourceName.String()) - pollingContext, cancel := context.WithTimeout(ctx, a.AzureClientSet.PollingDelay()) + pollingContext, cancel := context.WithTimeout(ctx, a.AzureClientSet.PollingDuration()) defer cancel() err := a.AzureClientSet.ImagesClient().DeleteThenPoll(pollingContext, imageID) if err != nil { diff --git a/builder/azure/common/client/azure_client_set.go b/builder/azure/common/client/azure_client_set.go index a1c2072c..1c7a07fa 100644 --- a/builder/azure/common/client/azure_client_set.go +++ b/builder/azure/common/client/azure_client_set.go @@ -43,7 +43,7 @@ type AzureClientSet interface { // SubscriptionID returns the subscription ID that this client set was created for SubscriptionID() string - PollingDelay() time.Duration + PollingDuration() time.Duration } var _ AzureClientSet = &azureClientSet{} @@ -52,7 +52,8 @@ type azureClientSet struct { sender autorest.Sender authorizer auth.Authorizer subscriptionID string - pollingDelay time.Duration + PollingDelay time.Duration + pollingDuration time.Duration ResourceManagerEndpoint string } @@ -81,7 +82,8 @@ func new(c Config, say func(string)) (*azureClientSet, error) { authorizer: authorizer, subscriptionID: c.SubscriptionID, sender: http.DefaultClient, - pollingDelay: time.Second, + PollingDelay: time.Second, + pollingDuration: time.Minute * 15, ResourceManagerEndpoint: *resourceManagerEndpoint, }, nil } @@ -90,8 +92,8 @@ func (s azureClientSet) SubscriptionID() string { return s.subscriptionID } -func (s azureClientSet) PollingDelay() time.Duration { - return s.pollingDelay +func (s azureClientSet) PollingDuration() time.Duration { + return s.pollingDuration } func (s azureClientSet) configureTrack1Client(c *autorest.Client) { @@ -113,49 +115,49 @@ func (s azureClientSet) MetadataClient() MetadataClientAPI { func (s azureClientSet) DisksClient() disks.DisksClient { c := disks.NewDisksClientWithBaseURI(s.ResourceManagerEndpoint) s.configureTrack1Client(&c.Client) - c.Client.PollingDelay = s.pollingDelay + c.Client.PollingDelay = s.PollingDelay return c } func (s azureClientSet) SnapshotsClient() snapshots.SnapshotsClient { c := snapshots.NewSnapshotsClientWithBaseURI(s.ResourceManagerEndpoint) s.configureTrack1Client(&c.Client) - c.Client.PollingDelay = s.pollingDelay + c.Client.PollingDelay = s.PollingDelay return c } func (s azureClientSet) ImagesClient() images.ImagesClient { c := images.NewImagesClientWithBaseURI(s.ResourceManagerEndpoint) s.configureTrack1Client(&c.Client) - c.Client.PollingDelay = s.pollingDelay + c.Client.PollingDelay = s.PollingDelay return c } func (s azureClientSet) VirtualMachinesClient() virtualmachines.VirtualMachinesClient { c := virtualmachines.NewVirtualMachinesClientWithBaseURI(s.ResourceManagerEndpoint) s.configureTrack1Client(&c.Client) - c.Client.PollingDelay = s.pollingDelay + c.Client.PollingDelay = s.PollingDelay return c } func (s azureClientSet) VirtualMachineImagesClient() virtualmachineimages.VirtualMachineImagesClient { c := virtualmachineimages.NewVirtualMachineImagesClientWithBaseURI(s.ResourceManagerEndpoint) s.configureTrack1Client(&c.Client) - c.Client.PollingDelay = s.pollingDelay + c.Client.PollingDelay = s.PollingDelay return c } func (s azureClientSet) GalleryImagesClient() galleryimages.GalleryImagesClient { c := galleryimages.NewGalleryImagesClientWithBaseURI(s.ResourceManagerEndpoint) s.configureTrack1Client(&c.Client) - c.Client.PollingDelay = s.pollingDelay + c.Client.PollingDelay = s.PollingDelay return c } func (s azureClientSet) GalleryImageVersionsClient() galleryimageversions.GalleryImageVersionsClient { c := galleryimageversions.NewGalleryImageVersionsClientWithBaseURI(s.ResourceManagerEndpoint) s.configureTrack1Client(&c.Client) - c.Client.PollingDelay = s.pollingDelay + c.Client.PollingDelay = s.PollingDelay return c } diff --git a/builder/azure/common/client/azure_client_set_mock.go b/builder/azure/common/client/azure_client_set_mock.go index dca903c0..1dfc03f3 100644 --- a/builder/azure/common/client/azure_client_set_mock.go +++ b/builder/azure/common/client/azure_client_set_mock.go @@ -28,7 +28,7 @@ type AzureClientSetMock struct { GalleryImageVersionsClientMock galleryimageversions.GalleryImageVersionsClient MetadataClientMock MetadataClientAPI SubscriptionIDMock string - PollingDelayMock time.Duration + PollingDurationMock time.Duration } // DisksClient returns a DisksClient @@ -76,6 +76,6 @@ func (m *AzureClientSetMock) SubscriptionID() string { return m.SubscriptionIDMock } -func (m *AzureClientSetMock) PollingDelay() time.Duration { - return m.PollingDelayMock +func (m *AzureClientSetMock) PollingDuration() time.Duration { + return m.PollingDurationMock } diff --git a/builder/azure/common/constants/stateBag.go b/builder/azure/common/constants/stateBag.go index a0fbffb4..4670bdb0 100644 --- a/builder/azure/common/constants/stateBag.go +++ b/builder/azure/common/constants/stateBag.go @@ -20,22 +20,22 @@ const ( ) const ( - 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" - ArmOSDiskUri string = "arm.OSDiskUri" - 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" + 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" + ArmOSDiskUri string = "arm.OSDiskUri" + 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" diff --git a/builder/azure/common/template/template.go b/builder/azure/common/template/template.go index 02cd42b5..3ed2ee10 100644 --- a/builder/azure/common/template/template.go +++ b/builder/azure/common/template/template.go @@ -132,6 +132,7 @@ type Properties struct { TypeHandlerVersion *string `json:"typeHandlerVersion,omitempty"` AutoUpgradeMinorVersion *bool `json:"autoUpgradeMinorVersion,omitempty"` Settings *CustomScriptSettings `json:"settings,omitempty"` + Attributes *Attributes `json:"attributes,omitempty"` } type CustomScriptSettings struct { @@ -151,6 +152,10 @@ type AccessPolicies struct { Permissions *Permissions `json:"permissions,omitempty"` } +type Attributes struct { + Exp int64 `json:"exp,omitempty"` +} + type Permissions struct { Keys *[]string `json:"keys,omitempty"` Secrets *[]string `json:"secrets,omitempty"` diff --git a/builder/azure/common/template/template_builder.go b/builder/azure/common/template/template_builder.go index d68528e2..7ba6388e 100644 --- a/builder/azure/common/template/template_builder.go +++ b/builder/azure/common/template/template_builder.go @@ -126,6 +126,18 @@ func (s *TemplateBuilder) BuildWindows(communicatorType string, keyVaultName str return nil } +func (s *TemplateBuilder) SetSecretExpiry(exp int64) error { + resource, err := s.getResourceByType(resourceKeyVaults) + if err != nil { + return err + } + resources := *resource.Resources + resources[0].Properties.Attributes = &Attributes{ + Exp: exp, + } + return nil +} + func (s *TemplateBuilder) SetIdentity(userAssignedManagedIdentities []string) error { resource, err := s.getResourceByType(resourceVirtualMachine) if err != nil { diff --git a/builder/azure/dtl/azure_client.go b/builder/azure/dtl/azure_client.go index b1bb0073..6ee98f3b 100644 --- a/builder/azure/dtl/azure_client.go +++ b/builder/azure/dtl/azure_client.go @@ -42,10 +42,10 @@ type AzureClient struct { galleryimageversions.GalleryImageVersionsClient galleryimages.GalleryImagesClient DtlMetaClient dtl.Client - - PollingDuration time.Duration + + PollingDuration time.Duration CustomImageCaptureTimeout time.Duration - SharedGalleryTimeout time.Duration + SharedGalleryTimeout time.Duration } func errorCapture(client *AzureClient) autorest.RespondDecorator { @@ -67,13 +67,13 @@ func errorCapture(client *AzureClient) autorest.RespondDecorator { func errorCaptureTrack2(client *AzureClient) client.ResponseMiddleware { return func(req *http.Request, resp *http.Response) (*http.Response, error) { body, bodyString := handleBody(resp.Body, math.MaxInt64) - resp.Body = body + resp.Body = body - errorResponse := newAzureErrorResponse(bodyString) - if errorResponse != nil { - client.LastError = *errorResponse - } - return resp, nil + errorResponse := newAzureErrorResponse(bodyString) + if errorResponse != nil { + client.LastError = *errorResponse + } + return resp, nil } } @@ -97,7 +97,6 @@ func NewAzureClient(ctx context.Context, subscriptionID string, azureClient.CustomImageCaptureTimeout = CustomImageCaptureTimeout azureClient.PollingDuration = PollingDuration azureClient.SharedGalleryTimeout = SharedGalleryTimeout - if cloud == nil || cloud.ResourceManager == nil { return nil, nil, fmt.Errorf("Azure Environment not configured correctly") diff --git a/builder/azure/dtl/inspector.go b/builder/azure/dtl/inspector.go index d0aeb42e..71f3ab2c 100644 --- a/builder/azure/dtl/inspector.go +++ b/builder/azure/dtl/inspector.go @@ -75,32 +75,32 @@ func byInspecting(maxlen int64) autorest.RespondDecorator { } func withInspectionTrack2(maxlen int64) client.RequestMiddleware { - return 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 r, nil + return 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 r, nil } } func byInspectingTrack2(maxlen int64) client.ResponseMiddleware { return func(req *http.Request, resp *http.Response) (*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, - }) + 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 resp, nil + return resp, nil } -} \ No newline at end of file +} diff --git a/builder/azure/dtl/template_factory.go b/builder/azure/dtl/template_factory.go index ad30aea7..40363e9e 100644 --- a/builder/azure/dtl/template_factory.go +++ b/builder/azure/dtl/template_factory.go @@ -101,9 +101,9 @@ func GetVirtualMachineDeployment(config *Config) (*labs.LabVirtualMachineCreatio } labMachine := &labs.LabVirtualMachineCreationParameter{ - Name: &config.tmpComputeName, - Location: &config.Location, - Tags: &config.AzureTags, + Name: &config.tmpComputeName, + Location: &config.Location, + Tags: &config.AzureTags, Properties: labMachineProps, } diff --git a/docs-partials/builder/azure/arm/Config-not-required.mdx b/docs-partials/builder/azure/arm/Config-not-required.mdx index 81076c9a..82f14e40 100644 --- a/docs-partials/builder/azure/arm/Config-not-required.mdx +++ b/docs-partials/builder/azure/arm/Config-not-required.mdx @@ -318,6 +318,10 @@ machine. For Linux this configures an SSH authorized key. For Windows this configures a WinRM certificate. +- `winrm_expiration_time` (duration string | ex: "1h5m2s") - A time duration with which to set the WinRM certificate to expire + This only works for Windows builds (valid time units include `s` for seconds, `m` for + minutes, and `h` for hours.) + - `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. From c6731f0856bc541e8fcec2d5728fd2fa2d767d27 Mon Sep 17 00:00:00 2001 From: Jenna Goldstrich Date: Thu, 3 Aug 2023 15:43:57 -0700 Subject: [PATCH 09/15] Fix rebasing error where we were logging for skip deletion always and remove VHD Deprecation --- builder/azure/arm/step_deploy_template.go | 4 ++-- builder/azure/dtl/builder.go | 6 +----- docs/builders/arm.mdx | 21 --------------------- 3 files changed, 3 insertions(+), 28 deletions(-) diff --git a/builder/azure/arm/step_deploy_template.go b/builder/azure/arm/step_deploy_template.go index 2e9d289b..8b3831f4 100644 --- a/builder/azure/arm/step_deploy_template.go +++ b/builder/azure/arm/step_deploy_template.go @@ -132,9 +132,9 @@ func (s *StepDeployTemplate) Cleanup(state multistep.StateBag) { "Name: %s\n"+ "Error: %s", imageName, err)) } + } else { + ui.Say(fmt.Sprintf("Skipping deletion -> %s : '%s' since 'keep_os_disk' is set to true", imageType, imageName)) } - ui.Say(fmt.Sprintf("Skipping deletion -> %s : '%s' since 'keep_os_disk' is set to true", imageType, imageName)) - var dataDisks []string if disks := state.Get(constants.ArmAdditionalDiskVhds); disks != nil { dataDisks = disks.([]string) diff --git a/builder/azure/dtl/builder.go b/builder/azure/dtl/builder.go index 25218985..e31782d4 100644 --- a/builder/azure/dtl/builder.go +++ b/builder/azure/dtl/builder.go @@ -130,11 +130,7 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) 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) diff --git a/docs/builders/arm.mdx b/docs/builders/arm.mdx index 488099fa..9437dddc 100644 --- a/docs/builders/arm.mdx +++ b/docs/builders/arm.mdx @@ -526,27 +526,6 @@ 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 From e3e59559d86d61e6a2fbb36789a00176a05deeb5 Mon Sep 17 00:00:00 2001 From: Jenna Goldstrich Date: Thu, 3 Aug 2023 15:45:30 -0700 Subject: [PATCH 10/15] Update goreleaser and codeowners to remove jenna's fork logic Okay linter.. --- .goreleaser.yml | 15 ++++----------- CODEOWNERS | 2 +- builder/azure/dtl/builder.go | 2 +- 3 files changed, 6 insertions(+), 13 deletions(-) diff --git a/.goreleaser.yml b/.goreleaser.yml index 0d2606bc..1c48ac23 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -94,17 +94,10 @@ checksum: name_template: '{{ .ProjectName }}_v{{ .Version }}_SHA256SUMS' algorithm: sha256 signs: - - artifacts: checksum - args: - # if you are using this is in a GitHub action or some other automated pipeline, you - # need to pass the batch flag to indicate its not interactive. - - "--batch" - - "--local-user" - - "{{ .Env.GPG_FINGERPRINT }}" - - "--output" - - "${signature}" - - "--detach-sign" - - "${artifact}" + - cmd: signore + args: ["sign", "--dearmor", "--file", "${artifact}", "--out", "${signature}"] + artifacts: checksum + signature: ${artifact}.sig release: # If you want to manually examine the release before its live, uncomment this line: # draft: true diff --git a/CODEOWNERS b/CODEOWNERS index 8847bfdc..e2487081 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -1 +1 @@ -* @JenGoldstrich @nywilken @lbajolet-hashicorp +* @hashicorp/packer diff --git a/builder/azure/dtl/builder.go b/builder/azure/dtl/builder.go index e31782d4..d350fb88 100644 --- a/builder/azure/dtl/builder.go +++ b/builder/azure/dtl/builder.go @@ -130,7 +130,7 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) 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) } } - } + } b.config.validateLocationZoneResiliency(ui.Say) From 06a217b7f73ed55f926ba1b1d43b177703ebf4f8 Mon Sep 17 00:00:00 2001 From: Jenna Goldstrich Date: Fri, 4 Aug 2023 15:13:43 -0700 Subject: [PATCH 11/15] Packer Plugin SDK v0.5.1 --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index cdb07410..3cc5bf74 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/google/go-cmp v0.5.9 github.com/hashicorp/go-azure-helpers v0.56.0 github.com/hashicorp/hcl/v2 v2.16.2 - github.com/hashicorp/packer-plugin-sdk v0.4.0 + github.com/hashicorp/packer-plugin-sdk v0.5.1 github.com/masterzen/winrm v0.0.0-20210623064412-3b76017826b0 github.com/mitchellh/mapstructure v1.5.0 github.com/mitchellh/reflectwalk v1.0.2 diff --git a/go.sum b/go.sum index 8aeee1d8..48e91fb7 100644 --- a/go.sum +++ b/go.sum @@ -295,8 +295,8 @@ github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO github.com/hashicorp/mdns v1.0.1/go.mod h1:4gW7WsVCke5TE7EPeYliwHlRUyBtfCwuFwuMg2DmyNY= github.com/hashicorp/memberlist v0.2.2 h1:5+RffWKwqJ71YPu9mWsF7ZOscZmwfasdA8kbdC7AO2g= github.com/hashicorp/memberlist v0.2.2/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= -github.com/hashicorp/packer-plugin-sdk v0.4.0 h1:UyLYe0y02D9wkOQ3FeeZWyFg2+mx2vLuWRGUL5xt50I= -github.com/hashicorp/packer-plugin-sdk v0.4.0/go.mod h1:uNhU3pmjM2ejgHYce/g4J+sa5rh81iYQztpGvGa5FOs= +github.com/hashicorp/packer-plugin-sdk v0.5.1 h1:ucXGWru98LsQrAWq8cEnaNN2Dvqw0HtyupAwNzrfy44= +github.com/hashicorp/packer-plugin-sdk v0.5.1/go.mod h1:OKziA4fQodpIq5HzOpRNt+vLpRJae61Z4uVFbeMLgd8= github.com/hashicorp/serf v0.9.5 h1:EBWvyu9tcRszt3Bxp3KNssBMP1KuHWyO51lz9+786iM= github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk= github.com/hashicorp/terraform-plugin-go v0.14.3 h1:nlnJ1GXKdMwsC8g1Nh05tK2wsC3+3BL/DBBxFEki+j0= From e1dc7d1b961c6f58a3535319306dedf829aa9302 Mon Sep 17 00:00:00 2001 From: Jenna Goldstrich Date: Tue, 8 Aug 2023 16:44:34 -0700 Subject: [PATCH 12/15] Partially implement Wilken's feedback --- builder/azure/arm/azure_client.go | 23 ++++++++++---------- builder/azure/arm/builder.go | 10 +++++---- builder/azure/arm/builder_acc_test.go | 2 +- builder/azure/arm/step_capture_image.go | 3 ++- builder/azure/dtl/azure_client.go | 17 ++++++++------- builder/azure/dtl/builder.go | 5 +++-- provisioner/azure-dtlartifact/provisioner.go | 2 +- 7 files changed, 34 insertions(+), 28 deletions(-) diff --git a/builder/azure/arm/azure_client.go b/builder/azure/arm/azure_client.go index 766cc9e3..92dda131 100644 --- a/builder/azure/arm/azure_client.go +++ b/builder/azure/arm/azure_client.go @@ -59,6 +59,7 @@ type AzureClient struct { InspectorMaxLength int LastError azureErrorResponse + ObjectID string PollingDuration time.Duration SharedGalleryTimeout time.Duration } @@ -92,6 +93,7 @@ func errorCaptureTrack2(client *AzureClient) client.ResponseMiddleware { } } +// Track 1 Legacy method, can be removed when all clients are track 2 func byConcatDecorators(decorators ...autorest.RespondDecorator) autorest.RespondDecorator { return func(r autorest.Responder) autorest.Responder { return autorest.DecorateResponder(r, decorators...) @@ -99,20 +101,19 @@ func byConcatDecorators(decorators ...autorest.RespondDecorator) autorest.Respon } // Returns an Azure Client used for the Azure Resource Manager -// Also returns the Azure object ID for the authentication method used in the build -func NewAzureClient(ctx context.Context, isVHDBuild bool, cloud *environments.Environment, sharedGalleryTimeout time.Duration, pollingDuration time.Duration, authOptions commonclient.AzureAuthOptions) (*AzureClient, *string, error) { +func NewAzureClient(ctx context.Context, isVHDBuild bool, cloud *environments.Environment, sharedGalleryTimeout time.Duration, pollingDuration time.Duration, authOptions commonclient.AzureAuthOptions) (*AzureClient, error) { var azureClient = &AzureClient{} azureClient.PollingDuration = pollingDuration azureClient.SharedGalleryTimeout = sharedGalleryTimeout maxlen := getInspectorMaxLength() if cloud == nil || cloud.ResourceManager == nil { - return nil, nil, fmt.Errorf("azure environment not configured correctly") + return nil, fmt.Errorf("azure environment not configured correctly") } resourceManagerEndpoint, _ := cloud.ResourceManager.Endpoint() resourceManagerAuthorizer, err := commonclient.BuildResourceManagerAuthorizer(ctx, authOptions, *cloud) if err != nil { - return nil, nil, err + return nil, err } trackTwoResponseMiddleware := []client.ResponseMiddleware{byInspectingTrack2(maxlen), errorCaptureTrack2(azureClient)} @@ -195,9 +196,8 @@ func NewAzureClient(ctx context.Context, isVHDBuild bool, cloud *environments.En c.Client.ResponseMiddlewares = &trackTwoResponseMiddleware c.Client.RequestMiddlewares = &trackTwoRequestMiddleware }) - if err != nil { - return nil, nil, err + return nil, err } azureClient.NetworkMetaClient = *networkMetaClient @@ -219,7 +219,7 @@ func NewAzureClient(ctx context.Context, isVHDBuild bool, cloud *environments.En if isVHDBuild { storageAccountAuthorizer, err := commonclient.BuildStorageAuthorizer(ctx, authOptions, *cloud) if err != nil { - return nil, nil, err + return nil, err } blobClient := giovanniBlobStorageSDK.New() @@ -232,16 +232,17 @@ func NewAzureClient(ctx context.Context, isVHDBuild bool, cloud *environments.En token, err := resourceManagerAuthorizer.Token(ctx, &http.Request{}) if err != nil { - return nil, nil, err + return nil, err } if token == nil { - return nil, nil, fmt.Errorf("unable to parse token from Azure Resource Manager") + return nil, fmt.Errorf("unable to parse token from Azure Resource Manager") } objectId, err := commonclient.GetObjectIdFromToken(token.AccessToken) if err != nil { - return nil, nil, err + return nil, err } - return azureClient, &objectId, nil + azureClient.ObjectID = objectId + return azureClient, nil } func getInspectorMaxLength() int64 { diff --git a/builder/azure/arm/builder.go b/builder/azure/arm/builder.go index 5c30e93c..0daf690e 100644 --- a/builder/azure/arm/builder.go +++ b/builder/azure/arm/builder.go @@ -98,9 +98,11 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) } ui.Message("Creating Azure Resource Manager (ARM) client ...") - azureClient, objectID, err := NewAzureClient( + // For VHD Builds we need to enable the Blob Azure Storage client + configureBlobClient := (b.config.ResourceGroupName != "" || b.config.StorageAccount != "") + azureClient, err := NewAzureClient( ctx, - (b.config.ResourceGroupName != "" || b.config.StorageAccount != ""), + configureBlobClient, b.config.ClientConfig.CloudEnvironment(), b.config.SharedGalleryTimeout, b.config.PollingDurationTimeout, @@ -115,9 +117,9 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) if err := resolver.Resolve(&b.config); err != nil { return nil, err } - + objectID := azureClient.ObjectID if b.config.ClientConfig.ObjectID == "" { - b.config.ClientConfig.ObjectID = *objectID + b.config.ClientConfig.ObjectID = objectID } else { ui.Message("You have provided Object_ID which is no longer needed, Azure Packer ARM builder determines this automatically using the Azure Access Token") } diff --git a/builder/azure/arm/builder_acc_test.go b/builder/azure/arm/builder_acc_test.go index 7bc76411..d2c59f24 100644 --- a/builder/azure/arm/builder_acc_test.go +++ b/builder/azure/arm/builder_acc_test.go @@ -382,7 +382,7 @@ func createTestAzureClient(t *testing.T) AzureClient { SubscriptionID: b.config.ClientConfig.SubscriptionID, } ui.Message("Creating test Azure Resource Manager (ARM) client ...") - azureClient, _, err := NewAzureClient( + azureClient, err := NewAzureClient( context.TODO(), true, b.config.ClientConfig.CloudEnvironment(), diff --git a/builder/azure/arm/step_capture_image.go b/builder/azure/arm/step_capture_image.go index f83532f4..aeaed2c0 100644 --- a/builder/azure/arm/step_capture_image.go +++ b/builder/azure/arm/step_capture_image.go @@ -5,6 +5,7 @@ package arm import ( "context" + "errors" "fmt" "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-01/images" @@ -80,7 +81,7 @@ func (s *StepCaptureImage) getVMID(ctx context.Context, vmId virtualmachines.Vir vmId := vmResponse.Model.Properties.VMId return *vmId, nil } - return "", nil + return "", errors.New("SDK returned empty model") } func (s *StepCaptureImage) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { diff --git a/builder/azure/dtl/azure_client.go b/builder/azure/dtl/azure_client.go index 6ee98f3b..30171dc1 100644 --- a/builder/azure/dtl/azure_client.go +++ b/builder/azure/dtl/azure_client.go @@ -46,6 +46,7 @@ type AzureClient struct { PollingDuration time.Duration CustomImageCaptureTimeout time.Duration SharedGalleryTimeout time.Duration + ObjectId string } func errorCapture(client *AzureClient) autorest.RespondDecorator { @@ -84,9 +85,8 @@ func byConcatDecorators(decorators ...autorest.RespondDecorator) autorest.Respon } // Returns an Azure Client used for the Azure Resource Manager -// Also returns the Azure object ID for the authentication method used in the build func NewAzureClient(ctx context.Context, subscriptionID string, - cloud *environments.Environment, SharedGalleryTimeout time.Duration, CustomImageCaptureTimeout time.Duration, PollingDuration time.Duration, authOptions azcommon.AzureAuthOptions) (*AzureClient, *string, error) { + cloud *environments.Environment, SharedGalleryTimeout time.Duration, CustomImageCaptureTimeout time.Duration, PollingDuration time.Duration, authOptions azcommon.AzureAuthOptions) (*AzureClient, error) { var azureClient = &AzureClient{} @@ -99,12 +99,12 @@ func NewAzureClient(ctx context.Context, subscriptionID string, azureClient.SharedGalleryTimeout = SharedGalleryTimeout if cloud == nil || cloud.ResourceManager == nil { - return nil, nil, fmt.Errorf("Azure Environment not configured correctly") + return nil, fmt.Errorf("Azure Environment not configured correctly") } resourceManagerEndpoint, _ := cloud.ResourceManager.Endpoint() resourceManagerAuthorizer, err := azcommon.BuildResourceManagerAuthorizer(ctx, authOptions, *cloud) if err != nil { - return nil, nil, err + return nil, err } dtlMetaClient := dtl.NewClientWithBaseURI(*resourceManagerEndpoint, func(c *autorest.Client) { c.Authorizer = authWrapper.AutorestAuthorizer(resourceManagerAuthorizer) @@ -143,18 +143,19 @@ func NewAzureClient(ctx context.Context, subscriptionID string, }) if err != nil { - return nil, nil, err + return nil, err } azureClient.NetworkMetaClient = *networkMetaClient token, err := resourceManagerAuthorizer.Token(ctx, &http.Request{}) if err != nil { - return nil, nil, err + return nil, err } objectId, err := azcommon.GetObjectIdFromToken(token.AccessToken) if err != nil { - return nil, nil, err + return nil, err } - return azureClient, &objectId, nil + azureClient.ObjectId = objectId + return azureClient, nil } const ( diff --git a/builder/azure/dtl/builder.go b/builder/azure/dtl/builder.go index d350fb88..b7720b49 100644 --- a/builder/azure/dtl/builder.go +++ b/builder/azure/dtl/builder.go @@ -85,7 +85,7 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) SubscriptionID: b.config.ClientConfig.SubscriptionID, } ui.Message("Creating Azure DevTestLab (DTL) client ...") - azureClient, objectId, err := NewAzureClient( + azureClient, err := NewAzureClient( ctx, b.config.ClientConfig.SubscriptionID, b.config.ClientConfig.CloudEnvironment(), @@ -102,8 +102,9 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) if err := resolver.Resolve(&b.config); err != nil { return nil, err } + objectId := azureClient.ObjectId if b.config.ClientConfig.ObjectID == "" { - b.config.ClientConfig.ObjectID = *objectId + b.config.ClientConfig.ObjectID = objectId } else { ui.Message("You have provided Object_ID which is no longer needed, azure packer builder determines this dynamically from the authentication token") } diff --git a/provisioner/azure-dtlartifact/provisioner.go b/provisioner/azure-dtlartifact/provisioner.go index 53d0d132..5be88c0b 100644 --- a/provisioner/azure-dtlartifact/provisioner.go +++ b/provisioner/azure-dtlartifact/provisioner.go @@ -136,7 +136,7 @@ func (p *Provisioner) Provision(ctx context.Context, ui packersdk.Ui, comm packe SubscriptionID: p.config.ClientConfig.SubscriptionID, } ui.Message("Creating Azure DevTestLab (DTL) client ...") - azureClient, _, err := dtlBuilder.NewAzureClient( + azureClient, err := dtlBuilder.NewAzureClient( ctx, p.config.ClientConfig.SubscriptionID, p.config.ClientConfig.CloudEnvironment(), From 4a5346709fece53a4b6d0e3ae51c2ef0cba0c5c6 Mon Sep 17 00:00:00 2001 From: Jenna Goldstrich Date: Wed, 9 Aug 2023 13:38:48 -0700 Subject: [PATCH 13/15] Add comments based on Wilken's feedback, don't panic on unexpected AuthType error --- builder/azure/arm/azure_client.go | 3 +++ builder/azure/arm/builder.go | 2 +- builder/azure/arm/inspector.go | 4 ++++ builder/azure/arm/step_capture_image.go | 3 ++- builder/azure/arm/step_deploy_template.go | 3 ++- builder/azure/chroot/step_create_new_diskset.go | 3 ++- builder/azure/chroot/step_get_source_image_name.go | 3 ++- builder/azure/chroot/step_resolve_plaform_image_version.go | 3 ++- builder/azure/chroot/step_verify_shared_image_destination.go | 5 +++-- builder/azure/chroot/step_verify_shared_image_source.go | 5 +++-- builder/azure/common/client/azure_authorizer.go | 2 +- builder/azure/common/client/config.go | 4 ++++ 12 files changed, 29 insertions(+), 11 deletions(-) diff --git a/builder/azure/arm/azure_client.go b/builder/azure/arm/azure_client.go index 92dda131..c6a14670 100644 --- a/builder/azure/arm/azure_client.go +++ b/builder/azure/arm/azure_client.go @@ -64,6 +64,7 @@ type AzureClient struct { SharedGalleryTimeout time.Duration } +// Error Capture Methods are used for parsing the returned error and setting is as the clients Last Error so we can log after failure func errorCapture(client *AzureClient) autorest.RespondDecorator { return func(r autorest.Responder) autorest.Responder { return autorest.ResponderFunc(func(resp *http.Response) error { @@ -80,6 +81,8 @@ func errorCapture(client *AzureClient) autorest.RespondDecorator { } } +// Track 1 (autorest) and Track 2 clients use different method signatures for defining their response/request middleware, these functions do the same thing but just are wrapped in an autorest handler or not. +// Go-Azure-SDK has endpoints supported on both Azure Track 1 and Azure Track 2, they will migrate them in time and we can consume those updates via upgrading our SDK func errorCaptureTrack2(client *AzureClient) client.ResponseMiddleware { return func(req *http.Request, resp *http.Response) (*http.Response, error) { body, bodyString := handleBody(resp.Body, math.MaxInt64) diff --git a/builder/azure/arm/builder.go b/builder/azure/arm/builder.go index 0daf690e..9c3de7fd 100644 --- a/builder/azure/arm/builder.go +++ b/builder/azure/arm/builder.go @@ -239,7 +239,7 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) return nil, fmt.Errorf("the parent Shared Gallery Image '%s' from which to source the managed image version to does not exist in the resource group '%s' or does not contain managed image '%s'", b.config.SharedGallery.GalleryName, b.config.SharedGallery.ResourceGroup, b.config.SharedGallery.ImageName) } if galleryImage.Model == nil { - return nil, fmt.Errorf("SDK returned empty model for gallery image") + return nil, errors.New(commonclient.SDK_NULL_MODEL_ERROR) } if galleryImage.Model.Properties.OsState == galleryimages.OperatingSystemStateTypesSpecialized { sourceImageSpecialized = true diff --git a/builder/azure/arm/inspector.go b/builder/azure/arm/inspector.go index 0020ae85..44736e3c 100644 --- a/builder/azure/arm/inspector.go +++ b/builder/azure/arm/inspector.go @@ -40,6 +40,10 @@ func handleBody(body io.ReadCloser, maxlen int64) (io.ReadCloser, string) { return ioutil.NopCloser(bytes.NewReader(b)), chop(b, maxlen) } +// WithInspection/ByInspection functions are used to Log requests and responses from Azure +// Same as with error capture there are track 2 and track 1 versions of these functions +// Once all endpoints we usea in go-azure-sdk are on track 2 clients +// We can delete the track 1 functions and rename the track 2 clients func withInspection(maxlen int64) autorest.PrepareDecorator { return func(p autorest.Preparer) autorest.Preparer { return autorest.PreparerFunc(func(r *http.Request) (*http.Request, error) { diff --git a/builder/azure/arm/step_capture_image.go b/builder/azure/arm/step_capture_image.go index aeaed2c0..813c43a7 100644 --- a/builder/azure/arm/step_capture_image.go +++ b/builder/azure/arm/step_capture_image.go @@ -10,6 +10,7 @@ import ( "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-01/images" "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-01/virtualmachines" + "github.com/hashicorp/packer-plugin-azure/builder/azure/common/client" "github.com/hashicorp/packer-plugin-azure/builder/azure/common/constants" "github.com/hashicorp/packer-plugin-sdk/multistep" packersdk "github.com/hashicorp/packer-plugin-sdk/packer" @@ -81,7 +82,7 @@ func (s *StepCaptureImage) getVMID(ctx context.Context, vmId virtualmachines.Vir vmId := vmResponse.Model.Properties.VMId return *vmId, nil } - return "", errors.New("SDK returned empty model") + return "", errors.New(client.SDK_NULL_MODEL_ERROR) } func (s *StepCaptureImage) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { diff --git a/builder/azure/arm/step_deploy_template.go b/builder/azure/arm/step_deploy_template.go index 8b3831f4..a64bc9ab 100644 --- a/builder/azure/arm/step_deploy_template.go +++ b/builder/azure/arm/step_deploy_template.go @@ -19,6 +19,7 @@ import ( "github.com/hashicorp/go-azure-sdk/resource-manager/network/2022-09-01/virtualnetworks" "github.com/hashicorp/go-azure-sdk/resource-manager/resources/2022-09-01/deploymentoperations" "github.com/hashicorp/go-azure-sdk/resource-manager/resources/2022-09-01/deployments" + "github.com/hashicorp/packer-plugin-azure/builder/azure/common/client" "github.com/hashicorp/packer-plugin-azure/builder/azure/common/constants" "github.com/hashicorp/packer-plugin-sdk/multistep" packersdk "github.com/hashicorp/packer-plugin-sdk/packer" @@ -198,7 +199,7 @@ func (s *StepDeployTemplate) getImageDetails(ctx context.Context, subscriptionId return "", "", err } if model := vm.Model; model == nil { - return "", "", errors.New("SDK returned empty model") + return "", "", errors.New(client.SDK_NULL_MODEL_ERROR) } if vm.Model.Properties.StorageProfile.OsDisk.Vhd != nil { imageType = "image" diff --git a/builder/azure/chroot/step_create_new_diskset.go b/builder/azure/chroot/step_create_new_diskset.go index 910b6ebc..dd3445d8 100644 --- a/builder/azure/chroot/step_create_new_diskset.go +++ b/builder/azure/chroot/step_create_new_diskset.go @@ -5,6 +5,7 @@ package chroot import ( "context" + "errors" "fmt" "log" "strings" @@ -237,7 +238,7 @@ func (s *StepCreateNewDiskset) getSharedImageGalleryVersion(ctx context.Context, return nil, err } if imageVersionResult.Model == nil { - return nil, fmt.Errorf("SDK returned empty model") + return nil, errors.New(client.SDK_NULL_MODEL_ERROR) } return imageVersionResult.Model, nil } diff --git a/builder/azure/chroot/step_get_source_image_name.go b/builder/azure/chroot/step_get_source_image_name.go index b4278c6a..b122dc57 100644 --- a/builder/azure/chroot/step_get_source_image_name.go +++ b/builder/azure/chroot/step_get_source_image_name.go @@ -5,6 +5,7 @@ package chroot import ( "context" + "errors" "fmt" "log" @@ -90,7 +91,7 @@ func (s *StepGetSourceImageName) getSharedImageGalleryVersion(ctx context.Contex return nil, err } if imageVersionResult.Model == nil { - return nil, fmt.Errorf("SDK returned empty model") + return nil, errors.New(client.SDK_NULL_MODEL_ERROR) } return imageVersionResult.Model, nil } diff --git a/builder/azure/chroot/step_resolve_plaform_image_version.go b/builder/azure/chroot/step_resolve_plaform_image_version.go index 273d25f9..4286e362 100644 --- a/builder/azure/chroot/step_resolve_plaform_image_version.go +++ b/builder/azure/chroot/step_resolve_plaform_image_version.go @@ -5,6 +5,7 @@ package chroot import ( "context" + "errors" "fmt" "log" "strings" @@ -79,7 +80,7 @@ func (s *StepResolvePlatformImageVersion) listVMImages(ctx context.Context, azcl return nil, err } if result.Model == nil { - return nil, fmt.Errorf("SDK returned empty model") + return nil, errors.New(client.SDK_NULL_MODEL_ERROR) } return result.Model, nil } diff --git a/builder/azure/chroot/step_verify_shared_image_destination.go b/builder/azure/chroot/step_verify_shared_image_destination.go index f1650701..fafcc4e8 100644 --- a/builder/azure/chroot/step_verify_shared_image_destination.go +++ b/builder/azure/chroot/step_verify_shared_image_destination.go @@ -5,6 +5,7 @@ package chroot import ( "context" + "errors" "fmt" "log" "strings" @@ -133,7 +134,7 @@ func (s *StepVerifySharedImageDestination) getGalleryImage(ctx context.Context, return nil, err } if res.Model == nil { - return nil, fmt.Errorf("SDK returned empty model") + return nil, errors.New(client.SDK_NULL_MODEL_ERROR) } return res.Model, nil } @@ -144,7 +145,7 @@ func (s *StepVerifySharedImageDestination) listGalleryVersions(ctx context.Conte return nil, err } if res.Items == nil { - return nil, fmt.Errorf("SDK returned empty model") + return nil, errors.New(client.SDK_NULL_MODEL_ERROR) } return res.Items, nil } diff --git a/builder/azure/chroot/step_verify_shared_image_source.go b/builder/azure/chroot/step_verify_shared_image_source.go index 51d494be..ed8ab6e1 100644 --- a/builder/azure/chroot/step_verify_shared_image_source.go +++ b/builder/azure/chroot/step_verify_shared_image_source.go @@ -5,6 +5,7 @@ package chroot import ( "context" + "errors" "fmt" "log" "strings" @@ -144,7 +145,7 @@ func (s *StepVerifySharedImageSource) getGalleryVersion(ctx context.Context, azc return nil, err } if res.Model == nil { - return nil, fmt.Errorf("SDK returned empty model") + return nil, errors.New(client.SDK_NULL_MODEL_ERROR) } return res.Model, nil } @@ -155,7 +156,7 @@ func (s *StepVerifySharedImageSource) getGalleryImage(ctx context.Context, azcli return nil, err } if res.Model == nil { - return nil, fmt.Errorf("SDK returned empty model") + return nil, errors.New(client.SDK_NULL_MODEL_ERROR) } return res.Model, nil } diff --git a/builder/azure/common/client/azure_authorizer.go b/builder/azure/common/client/azure_authorizer.go index 337e24b3..8d40b62b 100644 --- a/builder/azure/common/client/azure_authorizer.go +++ b/builder/azure/common/client/azure_authorizer.go @@ -77,7 +77,7 @@ func buildAuthorizer(ctx context.Context, authOpts AzureAuthOptions, env environ OIDCAssertionToken: authOpts.ClientJWT, } default: - panic("AuthType not set") + return nil, fmt.Errorf("Unexpected AuthType %s set when trying to create Azure Client", authOpts.AuthType) } authorizer, err := auth.NewAuthorizerFromCredentials(ctx, authConfig, api) if err != nil { diff --git a/builder/azure/common/client/config.go b/builder/azure/common/client/config.go index 6142816a..adc8936f 100644 --- a/builder/azure/common/client/config.go +++ b/builder/azure/common/client/config.go @@ -24,6 +24,10 @@ import ( packersdk "github.com/hashicorp/packer-plugin-sdk/packer" ) +// This error is thrown whenever the Azure SDK returns a null model with no error +// We do not expect this error to happen ever, but also don't want to throw a null pointer exception here. +const SDK_NULL_MODEL_ERROR = "Unexpected SDK response, please open an issue on the Azure plugin issue tracker" + // Config allows for various ways to authenticate Azure clients. When // `client_id` and `subscription_id` are specified in addition to one and only // one of the following: `client_secret`, `client_jwt`, `client_cert_path` -- From ae9ef526e1f81bf3ef24d4f7837e3433568425c2 Mon Sep 17 00:00:00 2001 From: Jenna Goldstrich Date: Thu, 10 Aug 2023 14:24:47 -0700 Subject: [PATCH 14/15] Implement Wilken's feedback on Null Model SDK Error --- builder/azure/arm/builder.go | 2 +- builder/azure/arm/step_capture_image.go | 3 +-- builder/azure/arm/step_deploy_template.go | 2 +- builder/azure/chroot/step_create_new_diskset.go | 3 +-- builder/azure/chroot/step_get_source_image_name.go | 3 +-- builder/azure/chroot/step_resolve_plaform_image_version.go | 3 +-- builder/azure/chroot/step_verify_shared_image_destination.go | 5 ++--- builder/azure/chroot/step_verify_shared_image_source.go | 5 ++--- builder/azure/common/client/config.go | 2 +- 9 files changed, 11 insertions(+), 17 deletions(-) diff --git a/builder/azure/arm/builder.go b/builder/azure/arm/builder.go index 9c3de7fd..2104501f 100644 --- a/builder/azure/arm/builder.go +++ b/builder/azure/arm/builder.go @@ -239,7 +239,7 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) return nil, fmt.Errorf("the parent Shared Gallery Image '%s' from which to source the managed image version to does not exist in the resource group '%s' or does not contain managed image '%s'", b.config.SharedGallery.GalleryName, b.config.SharedGallery.ResourceGroup, b.config.SharedGallery.ImageName) } if galleryImage.Model == nil { - return nil, errors.New(commonclient.SDK_NULL_MODEL_ERROR) + return nil, commonclient.NullModelSDKErr } if galleryImage.Model.Properties.OsState == galleryimages.OperatingSystemStateTypesSpecialized { sourceImageSpecialized = true diff --git a/builder/azure/arm/step_capture_image.go b/builder/azure/arm/step_capture_image.go index 813c43a7..35a08809 100644 --- a/builder/azure/arm/step_capture_image.go +++ b/builder/azure/arm/step_capture_image.go @@ -5,7 +5,6 @@ package arm import ( "context" - "errors" "fmt" "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-01/images" @@ -82,7 +81,7 @@ func (s *StepCaptureImage) getVMID(ctx context.Context, vmId virtualmachines.Vir vmId := vmResponse.Model.Properties.VMId return *vmId, nil } - return "", errors.New(client.SDK_NULL_MODEL_ERROR) + return "", client.NullModelSDKErr } func (s *StepCaptureImage) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { diff --git a/builder/azure/arm/step_deploy_template.go b/builder/azure/arm/step_deploy_template.go index a64bc9ab..112ba821 100644 --- a/builder/azure/arm/step_deploy_template.go +++ b/builder/azure/arm/step_deploy_template.go @@ -199,7 +199,7 @@ func (s *StepDeployTemplate) getImageDetails(ctx context.Context, subscriptionId return "", "", err } if model := vm.Model; model == nil { - return "", "", errors.New(client.SDK_NULL_MODEL_ERROR) + return "", "", client.NullModelSDKErr } if vm.Model.Properties.StorageProfile.OsDisk.Vhd != nil { imageType = "image" diff --git a/builder/azure/chroot/step_create_new_diskset.go b/builder/azure/chroot/step_create_new_diskset.go index dd3445d8..7fb01065 100644 --- a/builder/azure/chroot/step_create_new_diskset.go +++ b/builder/azure/chroot/step_create_new_diskset.go @@ -5,7 +5,6 @@ package chroot import ( "context" - "errors" "fmt" "log" "strings" @@ -238,7 +237,7 @@ func (s *StepCreateNewDiskset) getSharedImageGalleryVersion(ctx context.Context, return nil, err } if imageVersionResult.Model == nil { - return nil, errors.New(client.SDK_NULL_MODEL_ERROR) + return nil, client.NullModelSDKErr } return imageVersionResult.Model, nil } diff --git a/builder/azure/chroot/step_get_source_image_name.go b/builder/azure/chroot/step_get_source_image_name.go index b122dc57..b5b40c44 100644 --- a/builder/azure/chroot/step_get_source_image_name.go +++ b/builder/azure/chroot/step_get_source_image_name.go @@ -5,7 +5,6 @@ package chroot import ( "context" - "errors" "fmt" "log" @@ -91,7 +90,7 @@ func (s *StepGetSourceImageName) getSharedImageGalleryVersion(ctx context.Contex return nil, err } if imageVersionResult.Model == nil { - return nil, errors.New(client.SDK_NULL_MODEL_ERROR) + return nil, client.NullModelSDKErr } return imageVersionResult.Model, nil } diff --git a/builder/azure/chroot/step_resolve_plaform_image_version.go b/builder/azure/chroot/step_resolve_plaform_image_version.go index 4286e362..8221d966 100644 --- a/builder/azure/chroot/step_resolve_plaform_image_version.go +++ b/builder/azure/chroot/step_resolve_plaform_image_version.go @@ -5,7 +5,6 @@ package chroot import ( "context" - "errors" "fmt" "log" "strings" @@ -80,7 +79,7 @@ func (s *StepResolvePlatformImageVersion) listVMImages(ctx context.Context, azcl return nil, err } if result.Model == nil { - return nil, errors.New(client.SDK_NULL_MODEL_ERROR) + return nil, client.NullModelSDKErr } return result.Model, nil } diff --git a/builder/azure/chroot/step_verify_shared_image_destination.go b/builder/azure/chroot/step_verify_shared_image_destination.go index fafcc4e8..6e3b0c28 100644 --- a/builder/azure/chroot/step_verify_shared_image_destination.go +++ b/builder/azure/chroot/step_verify_shared_image_destination.go @@ -5,7 +5,6 @@ package chroot import ( "context" - "errors" "fmt" "log" "strings" @@ -134,7 +133,7 @@ func (s *StepVerifySharedImageDestination) getGalleryImage(ctx context.Context, return nil, err } if res.Model == nil { - return nil, errors.New(client.SDK_NULL_MODEL_ERROR) + return nil, client.NullModelSDKErr } return res.Model, nil } @@ -145,7 +144,7 @@ func (s *StepVerifySharedImageDestination) listGalleryVersions(ctx context.Conte return nil, err } if res.Items == nil { - return nil, errors.New(client.SDK_NULL_MODEL_ERROR) + return nil, client.NullModelSDKErr } return res.Items, nil } diff --git a/builder/azure/chroot/step_verify_shared_image_source.go b/builder/azure/chroot/step_verify_shared_image_source.go index ed8ab6e1..da1fa61d 100644 --- a/builder/azure/chroot/step_verify_shared_image_source.go +++ b/builder/azure/chroot/step_verify_shared_image_source.go @@ -5,7 +5,6 @@ package chroot import ( "context" - "errors" "fmt" "log" "strings" @@ -145,7 +144,7 @@ func (s *StepVerifySharedImageSource) getGalleryVersion(ctx context.Context, azc return nil, err } if res.Model == nil { - return nil, errors.New(client.SDK_NULL_MODEL_ERROR) + return nil, client.NullModelSDKErr } return res.Model, nil } @@ -156,7 +155,7 @@ func (s *StepVerifySharedImageSource) getGalleryImage(ctx context.Context, azcli return nil, err } if res.Model == nil { - return nil, errors.New(client.SDK_NULL_MODEL_ERROR) + return nil, client.NullModelSDKErr } return res.Model, nil } diff --git a/builder/azure/common/client/config.go b/builder/azure/common/client/config.go index adc8936f..e29726eb 100644 --- a/builder/azure/common/client/config.go +++ b/builder/azure/common/client/config.go @@ -26,7 +26,7 @@ import ( // This error is thrown whenever the Azure SDK returns a null model with no error // We do not expect this error to happen ever, but also don't want to throw a null pointer exception here. -const SDK_NULL_MODEL_ERROR = "Unexpected SDK response, please open an issue on the Azure plugin issue tracker" +var NullModelSDKErr = fmt.Errorf("Unexpected SDK response, please open an issue on the Azure plugin issue tracker") // Config allows for various ways to authenticate Azure clients. When // `client_id` and `subscription_id` are specified in addition to one and only From c5762d3d231e06ebfc877a40ea2afe205a814039 Mon Sep 17 00:00:00 2001 From: Jenna Goldstrich Date: Fri, 11 Aug 2023 12:04:38 -0700 Subject: [PATCH 15/15] Implement Lucas's feeback --- builder/azure/arm/config.go | 10 +++------ builder/azure/arm/config_test.go | 31 +++++++++++++++++++++++++-- builder/azure/common/client/config.go | 6 ------ 3 files changed, 32 insertions(+), 15 deletions(-) diff --git a/builder/azure/arm/config.go b/builder/azure/arm/config.go index 3445abca..1bcafdb0 100644 --- a/builder/azure/arm/config.go +++ b/builder/azure/arm/config.go @@ -1236,6 +1236,7 @@ func assertRequiredParametersSet(c *Config, errs *packersdk.MultiError) { } } + validImageVersion := regexp.MustCompile(`^[0-9]+\.[0-9]+\.[0-9]+$`) if c.SharedGalleryDestination.SigDestinationGalleryName != "" { if c.SharedGalleryDestination.SigDestinationResourceGroup == "" { errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("A resource_group must be specified for shared_image_gallery_destination")) @@ -1243,13 +1244,8 @@ func assertRequiredParametersSet(c *Config, errs *packersdk.MultiError) { 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")) - } else { - validImageVersion := regexp.MustCompile(`[0-9]*\.[0-9]*\.[0-9]*$`) - if !validImageVersion.Match([]byte(c.SharedGalleryDestination.SigDestinationImageVersion)) { - errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("An image_version must follow Major(int).Minor(int).Patch(int) format")) - } + if !validImageVersion.Match([]byte(c.SharedGalleryDestination.SigDestinationImageVersion)) { + errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("An image_version must be specified for shared_image_gallery_destination and must follow the Major(int).Minor(int).Patch(int) format")) } if c.SharedGalleryDestination.SigDestinationSubscription == "" { c.SharedGalleryDestination.SigDestinationSubscription = c.ClientConfig.SubscriptionID diff --git a/builder/azure/arm/config_test.go b/builder/azure/arm/config_test.go index d8bbd796..1d58a1c5 100644 --- a/builder/azure/arm/config_test.go +++ b/builder/azure/arm/config_test.go @@ -2092,6 +2092,33 @@ func TestConfigShouldAllowSharedImageGalleryOptions(t *testing.T) { } } +func TestConfigShouldRejectSharedImageGalleryDestinationNoVersionSet(t *testing.T) { + config := map[string]interface{}{ + "location": "ignore", + "subscription_id": "ignore", + "os_type": "linux", + "image_sku": "ignore", + "image_offer": "ignore", + "image_publisher": "ignore", + "shared_image_gallery_destination": map[string]string{ + "resource_group": "ignore", + "gallery_name": "ignore", + "image_name": "ignore", + "replication_regions": "ignore", + }, + } + + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) + if err == nil { + t.Fatal("expected config to reject invalid shared image gallery destination version", err) + } + errorMessage := "An image_version must be specified for shared_image_gallery_destination and must follow the Major(int).Minor(int).Patch(int) format" + if !strings.Contains(err.Error(), errorMessage) { + t.Errorf("expected config to reject with error containing %s but got %s", errorMessage, err) + } +} + func TestConfigShouldRejectSharedImageGalleryDestinationInvalidVersion(t *testing.T) { config := map[string]interface{}{ "location": "ignore", @@ -2104,7 +2131,7 @@ func TestConfigShouldRejectSharedImageGalleryDestinationInvalidVersion(t *testin "resource_group": "ignore", "gallery_name": "ignore", "image_name": "ignore", - "image_version": "not semver", + "image_version": "a.0.1", "replication_regions": "ignore", }, } @@ -2114,7 +2141,7 @@ func TestConfigShouldRejectSharedImageGalleryDestinationInvalidVersion(t *testin if err == nil { t.Fatal("expected config to reject invalid shared image gallery destination version", err) } - errorMessage := "An image_version must follow Major(int).Minor(int).Patch(int) format" + errorMessage := "An image_version must be specified for shared_image_gallery_destination and must follow the Major(int).Minor(int).Patch(int) format" if !strings.Contains(err.Error(), errorMessage) { t.Errorf("expected config to reject with error containing %s but got %s", errorMessage, err) } diff --git a/builder/azure/common/client/config.go b/builder/azure/common/client/config.go index e29726eb..0e3ff5c7 100644 --- a/builder/azure/common/client/config.go +++ b/builder/azure/common/client/config.go @@ -208,18 +208,12 @@ func (c Config) Validate(errs *packersdk.MultiError) { 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("%d %d, client_jwt will expire within 5 minutes, please use a JWT that is valid for at least 5 minutes", claims.ExpiresAt, time.Now().Add(5*time.Minute).Unix())) - //} 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")) }