├── .go-version ├── version └── version.go ├── .github ├── CODE_OF_CONDUCT.md ├── SUPPORT.md ├── workflows │ ├── test.yml │ ├── release.yml │ └── acctest.yml └── ISSUE_TEMPLATE.md ├── scripts ├── gogetcookie.sh ├── gofmtcheck.sh ├── errcheck.sh └── changelog-links.sh ├── OWNERS.md ├── .gitignore ├── docs ├── resources │ ├── volume.md │ ├── volume_attachment.md │ ├── user_api_key.md │ ├── project_api_key.md │ ├── vlan.md │ ├── organization.md │ ├── device_network_type.md │ ├── ssh_key.md │ ├── gateway.md │ ├── port.md │ ├── ip_attachment.md │ ├── project_ssh_key.md │ ├── connection.md │ ├── spot_market_request.md │ └── vrf.md ├── data-sources │ ├── volume.md │ ├── spot_market_price.md │ ├── operating_system.md │ ├── gateway.md │ ├── vrf.md │ ├── organization.md │ ├── metro.md │ ├── project_ssh_key.md │ ├── facility.md │ ├── port.md │ ├── reserved_ip_block.md │ ├── hardware_reservation.md │ ├── vlan.md │ ├── project.md │ ├── device_bgp_neighbors.md │ ├── ip_block_ranges.md │ ├── precreated_ip_block.md │ ├── spot_market_request.md │ ├── virtual_circuit.md │ ├── connection.md │ └── device.md └── index.md ├── main.go ├── metal ├── resource_metal_user_api_key.go ├── sweeper_test.go ├── resource_metal_project_ssh_key.go ├── volume_stub.go ├── datasource_metal_plans_test.go ├── provider_test.go ├── datasource_metal_spot_market_price_test.go ├── resource_metal_volume_attachment.go ├── datasource_metal_gateway_test.go ├── mutexkv.go ├── utils.go ├── datasource_metal_project_test.go ├── resource_metal_project_api_key_test.go ├── datasource_metal_reserved_ip_block_test.go ├── datasource_metal_vrf.go ├── datasource_metal_gateway.go ├── datasource_metal_operating_system_test.go ├── datasource_metal_ip_block_ranges_test.go ├── datasource_metal_precreated_ip_block_test.go ├── datasource_metal_metro.go ├── datasource_metal_spot_market_price.go ├── datasource_metal_organization_test.go ├── datasource_metal_metro_test.go ├── datasource_metal_volume.go ├── internal │ └── datalist │ │ ├── sort.go │ │ └── values.go ├── resource_metal_user_api_key_test.go ├── datasource_metal_port.go ├── datasource_metal_port_test.go ├── resource_metal_project_ssh_key_test.go ├── resource_metal_ip_attachment.go ├── resource_metal_volume.go ├── datasource_metal_operating_system.go ├── datasource_metal_facility_test.go ├── datasource_metal_spot_market_request_test.go ├── resource_metal_bgp_session.go ├── config.go ├── resource_metal_bgp_setup_test.go ├── datasource_metal_project_ssh_key.go ├── datasource_metal_spot_market_request.go ├── datasource_metal_hardware_reservation.go ├── resource_metal_gateway_test.go └── resource_metal_device_network_type.go ├── .goreleaser.yml ├── README.md ├── GNUmakefile ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md └── go.mod /.go-version: -------------------------------------------------------------------------------- 1 | 1.17.7 2 | -------------------------------------------------------------------------------- /version/version.go: -------------------------------------------------------------------------------- 1 | package version 2 | 3 | var ( 4 | // ProviderVersion is set at build-time in the release process 5 | ProviderVersion = "dev" 6 | ) 7 | -------------------------------------------------------------------------------- /.github/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | HashiCorp Community Guidelines apply to you when interacting with the community here on GitHub and contributing code. 4 | 5 | Please read the full text at https://www.hashicorp.com/community-guidelines 6 | -------------------------------------------------------------------------------- /.github/SUPPORT.md: -------------------------------------------------------------------------------- 1 | # Support 2 | 3 | Terraform is a mature project with a growing community. There are active, dedicated people willing to help you through various mediums. 4 | 5 | Take a look at those mediums listed at https://www.terraform.io/community.html 6 | -------------------------------------------------------------------------------- /scripts/gogetcookie.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | touch ~/.gitcookies 4 | chmod 0600 ~/.gitcookies 5 | 6 | git config --global http.cookiefile ~/.gitcookies 7 | 8 | tr , \\t <<\__END__ >>~/.gitcookies 9 | .googlesource.com,TRUE,/,TRUE,2147483647,o,git-paul.hashicorp.com=1/z7s05EYPudQ9qoe6dMVfmAVwgZopEkZBb1a2mA5QtHE 10 | __END__ 11 | -------------------------------------------------------------------------------- /scripts/gofmtcheck.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Check gofmt 4 | echo "==> Checking that code complies with gofmt requirements..." 5 | gofmt_files=$(gofmt -l `find . -name '*.go' | grep -v vendor`) 6 | if [[ -n ${gofmt_files} ]]; then 7 | echo 'gofmt needs running on the following files:' 8 | echo "${gofmt_files}" 9 | echo "You can use the command: \`make fmt\` to reformat code." 10 | exit 1 11 | fi 12 | 13 | exit 0 14 | -------------------------------------------------------------------------------- /OWNERS.md: -------------------------------------------------------------------------------- 1 | # Owners 2 | 3 | This project is governed by [Equinix Metal](https://metal.equinix.com) and benefits from a community of users that collaborate and contribute to its use in Kubernetes on Equinix Metal. 4 | 5 | Members of the Equinix Metal Github organization will strive to triage issues in a timely manner, see [SUPPORT.md](.github/SUPPORT.md) for details. 6 | 7 | See the [packethost/standards glossary](https://github.com/packethost/standards/blob/master/glossary.md#ownersmd) for more details about this file. 8 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Go Tests 2 | on: 3 | pull_request: 4 | jobs: 5 | test: 6 | runs-on: ubuntu-latest 7 | timeout-minutes: 10 8 | steps: 9 | - name: Set up Go 10 | uses: actions/setup-go@v2 11 | with: 12 | go-version: '1.17.7' 13 | - name: Check out code into the Go module directory 14 | uses: actions/checkout@v2 15 | - name: Get dependencies 16 | run: go mod download 17 | - name: Build 18 | run: go build -v . 19 | - name: TF tests 20 | run: go test -v -cover -parallel 4 ./metal 21 | 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.dll 2 | *.exe 3 | .DS_Store 4 | example.tf 5 | terraform.tfplan 6 | terraform.tfstate 7 | terraform-provider-metal 8 | bin/ 9 | modules-dev/ 10 | /pkg/ 11 | website/.vagrant 12 | website/.bundle 13 | website/build 14 | website/node_modules 15 | .vagrant/ 16 | *.backup 17 | ./*.tfstate 18 | .terraform/ 19 | *.log 20 | *.bak 21 | *~ 22 | .*.swp 23 | .idea 24 | .vscode 25 | *.iml 26 | *.test 27 | *.iml 28 | 29 | vendor 30 | website/vendor 31 | 32 | # Test exclusions 33 | !command/test-fixtures/**/*.tfstate 34 | !command/test-fixtures/**/.terraform/ 35 | 36 | # build binary exclusion 37 | terraform-provider-metal 38 | -------------------------------------------------------------------------------- /docs/resources/volume.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_title: "Equinix Metal: metal_volume" 3 | subcategory: "" 4 | description: |- 5 | (Removed) Provides an Equinix Metal Block Storage Volume Resource. 6 | --- 7 | 8 | !> **PROVIDER DEPRECATED:** Equinix Metal Provider is now Deprecated. [See the Metal provider section for more details](../index.md#equinix-metal-provider) on the new provider and available migration guides. 9 | 10 | Resource `metal_volume` was removed in version 3.0.0, and the API support was deprecated on June 1st 2021. See https://metal.equinix.com/developers/docs/storage/elastic-block-storage/#elastic-block-storage for more details. 11 | -------------------------------------------------------------------------------- /docs/data-sources/volume.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_title: "Equinix Metal: metal_volume" 3 | subcategory: "" 4 | description: |- 5 | (Removed) Provides an Equinix Metal Block Storage Volume Datasource. 6 | --- 7 | 8 | !> **PROVIDER DEPRECATED:** Equinix Metal Provider is now Deprecated. [See the Metal provider section for more details](../index.md#equinix-metal-provider) on the new provider and available migration guides. 9 | 10 | Datasource `metal_volume` was removed in version 3.0.0, and the API support was deprecated on June 1st 2021. See https://metal.equinix.com/developers/docs/storage/elastic-block-storage/#elastic-block-storage for more details. 11 | -------------------------------------------------------------------------------- /docs/resources/volume_attachment.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_title: "Equinix Metal: metal_volume_attachment" 3 | subcategory: "" 4 | description: |- 5 | (Removed) Provides attachment of volumes to devices in the Equinix Metal Host. 6 | --- 7 | 8 | !> **PROVIDER DEPRECATED:** Equinix Metal Provider is now Deprecated. [See the Metal provider section for more details](../index.md#equinix-metal-provider) on the new provider and available migration guides. 9 | 10 | Resource `metal_volume_attachment` was removed in version 3.0.0, and the API support was deprecated on June 1st 2021. See https://metal.equinix.com/developers/docs/storage/elastic-block-storage/#elastic-block-storage for more details. 11 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "flag" 6 | "log" 7 | 8 | "github.com/equinix/terraform-provider-metal/metal" 9 | "github.com/hashicorp/terraform-plugin-sdk/v2/plugin" 10 | ) 11 | 12 | func main() { 13 | var debugMode bool 14 | 15 | flag.BoolVar(&debugMode, "debug", false, "set to true to run the provider with support for debuggers like delve") 16 | flag.Parse() 17 | opts := &plugin.ServeOpts{ProviderFunc: metal.Provider} 18 | 19 | if debugMode { 20 | err := plugin.Debug(context.Background(), "registry.terraform.io/equinix/metal", opts) 21 | if err != nil { 22 | log.Fatal(err.Error()) 23 | } 24 | return 25 | } 26 | 27 | plugin.Serve(opts) 28 | } 29 | -------------------------------------------------------------------------------- /metal/resource_metal_user_api_key.go: -------------------------------------------------------------------------------- 1 | package metal 2 | 3 | import ( 4 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" 5 | ) 6 | 7 | func resourceMetalUserAPIKey() *schema.Resource { 8 | userKeySchema := schemaMetalAPIKey() 9 | userKeySchema["user_id"] = &schema.Schema{ 10 | Type: schema.TypeString, 11 | Computed: true, 12 | Description: "UUID of user owning this key", 13 | } 14 | return &schema.Resource{ 15 | DeprecationMessage: deprecatedProviderMsg, 16 | Create: resourceMetalAPIKeyCreate, 17 | Read: resourceMetalAPIKeyRead, 18 | Delete: resourceMetalAPIKeyDelete, 19 | Importer: &schema.ResourceImporter{ 20 | State: schema.ImportStatePassthrough, 21 | }, 22 | 23 | Schema: userKeySchema, 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /scripts/errcheck.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Check gofmt 4 | echo "==> Checking for unchecked errors..." 5 | 6 | if ! which errcheck > /dev/null; then 7 | echo "==> Installing errcheck..." 8 | go get -u github.com/kisielk/errcheck 9 | fi 10 | 11 | err_files=$(errcheck -ignoretests \ 12 | -ignore 'github.com/hashicorp/terraform/helper/schema:Set' \ 13 | -ignore 'bytes:.*' \ 14 | -ignore 'io:Close|Write' \ 15 | $(go list ./...| grep -v /vendor/)) 16 | 17 | if [[ -n ${err_files} ]]; then 18 | echo 'Unchecked errors found in the following places:' 19 | echo "${err_files}" 20 | echo "Please handle returned errors. You can check directly with \`make errcheck\`" 21 | exit 1 22 | fi 23 | 24 | exit 0 25 | -------------------------------------------------------------------------------- /metal/sweeper_test.go: -------------------------------------------------------------------------------- 1 | package metal 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "strings" 7 | "testing" 8 | 9 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" 10 | ) 11 | 12 | const tstResourcePrefix = "tfacc" 13 | 14 | func TestMain(m *testing.M) { 15 | resource.TestMain(m) 16 | } 17 | 18 | func sharedConfigForRegion(region string) (interface{}, error) { 19 | token := os.Getenv("METAL_AUTH_TOKEN") 20 | 21 | if token == "" { 22 | token = os.Getenv("PACKET_AUTH_TOKEN") 23 | } 24 | 25 | if token == "" { 26 | return nil, fmt.Errorf("you must set METAL_AUTH_TOKEN") 27 | } 28 | 29 | config := Config{ 30 | AuthToken: token, 31 | } 32 | 33 | return config.Client(), nil 34 | } 35 | 36 | func isSweepableTestResource(namePrefix string) bool { 37 | return strings.HasPrefix(namePrefix, tstResourcePrefix) 38 | } 39 | -------------------------------------------------------------------------------- /metal/resource_metal_project_ssh_key.go: -------------------------------------------------------------------------------- 1 | package metal 2 | 3 | import ( 4 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" 5 | ) 6 | 7 | func resourceMetalProjectSSHKey() *schema.Resource { 8 | pkeySchema := metalSSHKeyCommonFields() 9 | pkeySchema["project_id"] = &schema.Schema{ 10 | Type: schema.TypeString, 11 | Description: "The ID of parent project", 12 | ForceNew: true, 13 | Required: true, 14 | } 15 | return &schema.Resource{ 16 | DeprecationMessage: deprecatedProviderMsg, 17 | Create: resourceMetalSSHKeyCreate, 18 | Read: resourceMetalSSHKeyRead, 19 | Update: resourceMetalSSHKeyUpdate, 20 | Delete: resourceMetalSSHKeyDelete, 21 | Importer: &schema.ResourceImporter{ 22 | State: schema.ImportStatePassthrough, 23 | }, 24 | Schema: pkeySchema, 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /metal/volume_stub.go: -------------------------------------------------------------------------------- 1 | package metal 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | 7 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" 8 | ) 9 | 10 | const volumeRemovedMsgSuffix = "was removed in version 3.0.0, see https://metal.equinix.com/developers/docs/storage/elastic-block-storage/#elastic-block-storage" 11 | 12 | var ( 13 | resourceVolumeRemovedMsg = fmt.Sprintf("resource metal_volume %s", volumeRemovedMsgSuffix) 14 | dataSourceVolumeRemovedMsg = fmt.Sprintf("datasource metal_volume %s", volumeRemovedMsgSuffix) 15 | resourceVolumeAttachmentRemovedMsg = fmt.Sprintf("resource metal_volume_attachment %s", volumeRemovedMsgSuffix) 16 | ) 17 | 18 | func removedResourceOp(message string) func(d *schema.ResourceData, meta interface{}) error { 19 | return func(d *schema.ResourceData, meta interface{}) error { 20 | return errors.New(message) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /metal/datasource_metal_plans_test.go: -------------------------------------------------------------------------------- 1 | package metal 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" 8 | ) 9 | 10 | func TestAccDataSourcePlans_Basic(t *testing.T) { 11 | testSlug := "m2.xlarge.x86" 12 | resource.Test(t, resource.TestCase{ 13 | PreCheck: func() { testAccPreCheck(t) }, 14 | Providers: testAccProviders, 15 | Steps: []resource.TestStep{ 16 | { 17 | Config: testAccDataSourcePlansConfigBasic(testSlug), 18 | Check: resource.ComposeTestCheckFunc( 19 | resource.TestCheckResourceAttr( 20 | "data.metal_plans.test", "plans.0.slug", testSlug), 21 | ), 22 | }, 23 | }, 24 | }) 25 | } 26 | 27 | func testAccDataSourcePlansConfigBasic(slug string) string { 28 | return fmt.Sprintf(` 29 | data "metal_plans" "test" { 30 | filter { 31 | attribute = "slug" 32 | values = ["%s"] 33 | } 34 | } 35 | 36 | output "test" { 37 | value = data.metal_plans.test 38 | } 39 | `, slug) 40 | } 41 | -------------------------------------------------------------------------------- /scripts/changelog-links.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # This script rewrites [GH-nnnn]-style references in the CHANGELOG.md file to 4 | # be Markdown links to the given github issues. 5 | # 6 | # This is run during releases so that the issue references in all of the 7 | # released items are presented as clickable links, but we can just use the 8 | # easy [GH-nnnn] shorthand for quickly adding items to the "Unrelease" section 9 | # while merging things between releases. 10 | 11 | set -e 12 | 13 | if [[ ! -f CHANGELOG.md ]]; then 14 | echo "ERROR: CHANGELOG.md not found in pwd." 15 | echo "Please run this from the root of the terraform provider repository" 16 | exit 1 17 | fi 18 | 19 | if [[ `uname` == "Darwin" ]]; then 20 | echo "Using BSD sed" 21 | SED="sed -i.bak -E -e" 22 | else 23 | echo "Using GNU sed" 24 | SED="sed -i.bak -r -e" 25 | fi 26 | 27 | PROVIDER_URL="https:\/\/github.com\/equinix\/terraform-provider-metal\/issues" 28 | 29 | $SED "s/GH-([0-9]+)/\[#\1\]\($PROVIDER_URL\/\1\)/g" -e 's/\[\[#(.+)([0-9])\)]$/(\[#\1\2))/g' CHANGELOG.md 30 | 31 | rm CHANGELOG.md.bak 32 | -------------------------------------------------------------------------------- /metal/provider_test.go: -------------------------------------------------------------------------------- 1 | package metal 2 | 3 | import ( 4 | "os" 5 | "testing" 6 | "time" 7 | 8 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" 9 | ) 10 | 11 | var testAccProviders map[string]*schema.Provider 12 | var testAccProvider *schema.Provider 13 | 14 | func init() { 15 | testAccProvider = Provider() 16 | testAccProviders = map[string]*schema.Provider{ 17 | "metal": testAccProvider, 18 | } 19 | } 20 | 21 | func TestProvider(t *testing.T) { 22 | if err := Provider().InternalValidate(); err != nil { 23 | t.Fatalf("err: %s", err) 24 | } 25 | } 26 | 27 | /* 28 | func TestProvider_impl(t *testing.T) { 29 | var _ terraform.ResourceProvider = Provider() 30 | } 31 | */ 32 | 33 | func testAccPreCheck(t *testing.T) { 34 | v := os.Getenv("METAL_AUTH_TOKEN") 35 | 36 | if v == "" { 37 | v = os.Getenv("PACKET_AUTH_TOKEN") 38 | } 39 | 40 | if v == "" { 41 | t.Fatal("METAL_AUTH_TOKEN must be set for acceptance tests") 42 | } 43 | } 44 | 45 | func testDeviceTerminationTime() string { 46 | return time.Now().UTC().Add(60 * time.Minute).Format(time.RFC3339) 47 | } 48 | -------------------------------------------------------------------------------- /metal/datasource_metal_spot_market_price_test.go: -------------------------------------------------------------------------------- 1 | package metal 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" 8 | ) 9 | 10 | func TestAccDataSourceMetalSpotPrice_Basic(t *testing.T) { 11 | resource.ParallelTest(t, resource.TestCase{ 12 | PreCheck: func() { testAccPreCheck(t) }, 13 | Providers: testAccProviders, 14 | CheckDestroy: testAccCheckMetalSpotMarketRequestDestroy, 15 | Steps: []resource.TestStep{ 16 | { 17 | Config: testDataSourceMetalSpotMarketPrice(), 18 | Check: resource.ComposeTestCheckFunc( 19 | resource.TestCheckResourceAttrSet( 20 | "data.metal_spot_market_price.metro", "price"), 21 | resource.TestCheckResourceAttrSet( 22 | "data.metal_spot_market_price.facility", "price"), 23 | ), 24 | }, 25 | }, 26 | }) 27 | } 28 | 29 | func testDataSourceMetalSpotMarketPrice() string { 30 | return fmt.Sprintf(` 31 | data "metal_spot_market_price" "metro" { 32 | metro = "da" 33 | plan = "c3.medium.x86" 34 | } 35 | 36 | data "metal_spot_market_price" "facility" { 37 | facility = "da11" 38 | plan = "c3.medium.x86" 39 | } 40 | `) 41 | } 42 | -------------------------------------------------------------------------------- /metal/resource_metal_volume_attachment.go: -------------------------------------------------------------------------------- 1 | package metal 2 | 3 | import ( 4 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" 5 | "github.com/packethost/packngo" 6 | ) 7 | 8 | func resourceMetalVolumeAttachment() *schema.Resource { 9 | return &schema.Resource{ 10 | Create: removedResourceOp(resourceVolumeAttachmentRemovedMsg), 11 | Read: removedResourceOp(resourceVolumeAttachmentRemovedMsg), 12 | DeprecationMessage: "Volumes are deprecated, see https://metal.equinix.com/developers/docs/resilience-recovery/elastic-block-storage/#elastic-block-storage", 13 | Delete: resourceMetalVolumeAttachmentDelete, 14 | Importer: &schema.ResourceImporter{ 15 | State: schema.ImportStatePassthrough, 16 | }, 17 | 18 | Schema: map[string]*schema.Schema{ 19 | "device_id": { 20 | Type: schema.TypeString, 21 | Required: true, 22 | ForceNew: true, 23 | }, 24 | 25 | "volume_id": { 26 | Type: schema.TypeString, 27 | Required: true, 28 | ForceNew: true, 29 | }, 30 | }, 31 | } 32 | } 33 | 34 | func resourceMetalVolumeAttachmentDelete(d *schema.ResourceData, meta interface{}) error { 35 | client := meta.(*packngo.Client) 36 | resp, err := client.VolumeAttachments.Delete(d.Id()) 37 | return ignoreResponseErrors(httpForbidden, httpNotFound)(resp, err) 38 | } 39 | -------------------------------------------------------------------------------- /docs/data-sources/spot_market_price.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_title: "Equinix Metal: spot_market_price" 3 | subcategory: "" 4 | description: |- 5 | Get an Equinix Metal Spot Market Price 6 | --- 7 | 8 | # metal_operating_system (Data Source) 9 | 10 | !> **PROVIDER DEPRECATED:** Equinix Metal Provider is now Deprecated. Please consider using [`equinix_metal_operating_system`](https://registry.terraform.io/providers/equinix/equinix/latest/docs/data-sources/equinix_metal_operating_system) data source from the [Equinix provider](https://registry.terraform.io/providers/equinix/equinix/latest/docs) instead of `metal_operating_system`. [See the Metal provider section for more details](../index.md#equinix-metal-provider) on the new provider and available migration guides. 11 | 12 | Use this data source to get Equinix Metal Spot Market Price for a plan. 13 | 14 | ## Example Usage 15 | 16 | Lookup by facility: 17 | 18 | ```hcl 19 | data "metal_spot_market_price" "example" { 20 | facility = "ny5" 21 | plan = "c3.small.x86" 22 | } 23 | ``` 24 | 25 | Lookup by metro: 26 | 27 | ```hcl 28 | data "metal_spot_market_price" "example" { 29 | metro = "sv" 30 | plan = "c3.small.x86" 31 | } 32 | ``` 33 | 34 | ## Argument Reference 35 | 36 | * `plan` - (Required) Name of the plan. 37 | * `facility` - (Optional) Name of the facility. 38 | * `metro` - (Optional) Name of the metro. 39 | 40 | ## Attributes Reference 41 | 42 | * `price` - Current spot market price for given plan in given facility. 43 | -------------------------------------------------------------------------------- /metal/datasource_metal_gateway_test.go: -------------------------------------------------------------------------------- 1 | package metal 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" 8 | ) 9 | 10 | func testAccDataSourceMetalGatewayConfig_PrivateIPv4() string { 11 | return fmt.Sprintf(` 12 | resource "metal_project" "test" { 13 | name = "tfacc-pro-gateway-test" 14 | } 15 | 16 | resource "metal_vlan" "test" { 17 | description = "%s-vlan in SV" 18 | metro = "sv" 19 | project_id = metal_project.test.id 20 | } 21 | 22 | resource "metal_gateway" "test" { 23 | project_id = metal_project.test.id 24 | vlan_id = metal_vlan.test.id 25 | private_ipv4_subnet_size = 8 26 | } 27 | 28 | data "metal_gateway" "test" { 29 | gateway_id = metal_gateway.test.id 30 | } 31 | `, tstResourcePrefix) 32 | } 33 | 34 | func TestAccDataSourceMetalGateway_PrivateIPv4(t *testing.T) { 35 | resource.ParallelTest(t, resource.TestCase{ 36 | PreCheck: func() { testAccPreCheck(t) }, 37 | Providers: testAccProviders, 38 | CheckDestroy: testAccCheckMetalGatewayDestroyed, 39 | Steps: []resource.TestStep{ 40 | { 41 | Config: testAccDataSourceMetalGatewayConfig_PrivateIPv4(), 42 | Check: resource.ComposeTestCheckFunc( 43 | resource.TestCheckResourceAttrPair( 44 | "data.metal_gateway.test", "project_id", 45 | "metal_project.test", "id"), 46 | resource.TestCheckResourceAttr( 47 | "data.metal_gateway.test", "private_ipv4_subnet_size", "8"), 48 | ), 49 | }, 50 | }, 51 | }) 52 | } 53 | -------------------------------------------------------------------------------- /metal/mutexkv.go: -------------------------------------------------------------------------------- 1 | package metal 2 | 3 | import ( 4 | "log" 5 | "sync" 6 | ) 7 | 8 | // MutexKV is a simple key/value store for arbitrary mutexes. It can be used to 9 | // serialize changes across arbitrary collaborators that share knowledge of the 10 | // keys they must serialize on. 11 | // 12 | // The initial use case is to let aws_security_group_rule resources serialize 13 | // their access to individual security groups based on SG ID. 14 | type MutexKV struct { 15 | lock sync.Mutex 16 | store map[string]*sync.Mutex 17 | } 18 | 19 | // Locks the mutex for the given key. Caller is responsible for calling Unlock 20 | // for the same key 21 | func (m *MutexKV) Lock(key string) { 22 | log.Printf("[DEBUG] Locking %q", key) 23 | m.get(key).Lock() 24 | log.Printf("[DEBUG] Locked %q", key) 25 | } 26 | 27 | // Unlock the mutex for the given key. Caller must have called Lock for the same key first 28 | func (m *MutexKV) Unlock(key string) { 29 | log.Printf("[DEBUG] Unlocking %q", key) 30 | m.get(key).Unlock() 31 | log.Printf("[DEBUG] Unlocked %q", key) 32 | } 33 | 34 | // Returns a mutex for the given key, no guarantee of its lock status 35 | func (m *MutexKV) get(key string) *sync.Mutex { 36 | m.lock.Lock() 37 | defer m.lock.Unlock() 38 | mutex, ok := m.store[key] 39 | if !ok { 40 | mutex = &sync.Mutex{} 41 | m.store[key] = mutex 42 | } 43 | return mutex 44 | } 45 | 46 | // Returns a properly initalized MutexKV 47 | func NewMutexKV() *MutexKV { 48 | return &MutexKV{ 49 | store: make(map[string]*sync.Mutex), 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /metal/utils.go: -------------------------------------------------------------------------------- 1 | package metal 2 | 3 | import ( 4 | "strconv" 5 | "strings" 6 | ) 7 | 8 | func contains(s []string, e string) bool { 9 | for _, a := range s { 10 | if a == e { 11 | return true 12 | } 13 | } 14 | return false 15 | } 16 | 17 | func stringArrToIfArr(sli []string) []interface{} { 18 | var arr []interface{} 19 | for _, v := range sli { 20 | arr = append(arr, v) 21 | } 22 | return arr 23 | } 24 | 25 | func convertStringArr(ifaceArr []interface{}) []string { 26 | var arr []string 27 | for _, v := range ifaceArr { 28 | if v == nil { 29 | continue 30 | } 31 | arr = append(arr, v.(string)) 32 | } 33 | return arr 34 | } 35 | 36 | func convertIntArr(ifaceArr []interface{}) []string { 37 | var arr []string 38 | for _, v := range ifaceArr { 39 | if v == nil { 40 | continue 41 | } 42 | arr = append(arr, strconv.Itoa(v.(int))) 43 | } 44 | return arr 45 | } 46 | 47 | func convertIntArr2(ifaceArr []interface{}) []int { 48 | var arr []int 49 | for _, v := range ifaceArr { 50 | if v == nil { 51 | continue 52 | } 53 | arr = append(arr, v.(int)) 54 | } 55 | return arr 56 | } 57 | 58 | func toLower(v interface{}) string { 59 | return strings.ToLower(v.(string)) 60 | } 61 | 62 | // from https://stackoverflow.com/a/45428032 63 | func difference(a, b []string) []string { 64 | mb := make(map[string]struct{}, len(b)) 65 | for _, x := range b { 66 | mb[x] = struct{}{} 67 | } 68 | var diff []string 69 | for _, x := range a { 70 | if _, found := mb[x]; !found { 71 | diff = append(diff, x) 72 | } 73 | } 74 | return diff 75 | } 76 | -------------------------------------------------------------------------------- /docs/resources/user_api_key.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_title: "Equinix Metal: Metal User API Key" 3 | subcategory: "" 4 | description: |- 5 | Create Equinix Metal User API Keys 6 | --- 7 | 8 | # metal_user_api_key (Resource) 9 | 10 | !> **PROVIDER DEPRECATED:** Equinix Metal Provider is now Deprecated. Please consider using [`equinix_metal_user_api_key`](https://registry.terraform.io/providers/equinix/equinix/latest/docs/resources/equinix_metal_user_api_key) resource from the [Equinix provider](https://registry.terraform.io/providers/equinix/equinix/latest/docs) instead of `metal_user_api_key`. [See the Metal provider section for more details](../index.md#equinix-metal-provider) on the new provider and available migration guides. 11 | 12 | Use this resource to create Metal User API Key resources in Equinix Metal. Each API key contains a token which can be used for authentication in Equinix Metal HTTP API (in HTTP request header `X-Auth-Token`). 13 | 14 | Read-only keys only allow to list and view existing resources, read-write keys can also be used to create resources. 15 | 16 | ## Example Usage 17 | 18 | ```hcl 19 | 20 | # Create a new read-only API key 21 | 22 | resource "metal_user_api_key" "test" { 23 | description = "Read-only user key" 24 | read_only = true 25 | } 26 | ``` 27 | 28 | ## Argument Reference 29 | 30 | * `description` - Description string for the User API Key resource 31 | * `read-only` - Flag indicating whether the API key shoud be read-only 32 | 33 | ## Attributes Reference 34 | 35 | * `user_id` - UUID of the owner of the API key 36 | * `token` - API token which can be used in Equinix Metal API clients 37 | -------------------------------------------------------------------------------- /metal/datasource_metal_project_test.go: -------------------------------------------------------------------------------- 1 | package metal 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" 8 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" 9 | "github.com/packethost/packngo" 10 | ) 11 | 12 | func testAccCheckMetalDataSourceProject_Basic(r string) string { 13 | return fmt.Sprintf(` 14 | resource "metal_project" "foobar" { 15 | name = "tfacc-pro-%s" 16 | bgp_config { 17 | deployment_type = "local" 18 | md5 = "2SFsdfsg43" 19 | asn = 65000 20 | } 21 | } 22 | 23 | data metal_project "test" { 24 | project_id = metal_project.foobar.id 25 | } 26 | 27 | `, r) 28 | } 29 | 30 | func TestAccMetalDataSourceProject_Basic(t *testing.T) { 31 | var project packngo.Project 32 | rn := acctest.RandStringFromCharSet(12, "abcdef0123456789") 33 | 34 | resource.ParallelTest(t, resource.TestCase{ 35 | PreCheck: func() { testAccPreCheck(t) }, 36 | Providers: testAccProviders, 37 | CheckDestroy: testAccCheckMetalProjectDestroy, 38 | Steps: []resource.TestStep{ 39 | { 40 | Config: testAccCheckMetalDataSourceProject_Basic(rn), 41 | Check: resource.ComposeTestCheckFunc( 42 | testAccCheckMetalProjectExists("metal_project.foobar", &project), 43 | resource.TestCheckResourceAttr( 44 | "metal_project.foobar", "name", fmt.Sprintf("tfacc-pro-%s", rn)), 45 | resource.TestCheckResourceAttr( 46 | "metal_project.foobar", "bgp_config.0.md5", 47 | "2SFsdfsg43"), 48 | resource.TestCheckResourceAttrPair( 49 | "metal_project.foobar", "id", 50 | "data.metal_project.test", "id"), 51 | ), 52 | }, 53 | }, 54 | }) 55 | } 56 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | # This GitHub action can publish assets for release when a tag is created. 2 | # Currently its setup to run on any tag that matches the pattern "v*" (ie. v0.1.0). 3 | # 4 | # This uses an action (paultyng/ghaction-import-gpg) that assumes you set your 5 | # private key in the `GPG_PRIVATE_KEY` secret and passphrase in the `PASSPHRASE` 6 | # secret. If you would rather own your own GPG handling, please fork this action 7 | # or use an alternative one for key handling. 8 | # 9 | # You will need to pass the `--batch` flag to `gpg` in your signing step 10 | # in `goreleaser` to indicate this is being used in a non-interactive mode. 11 | # 12 | name: release 13 | on: 14 | push: 15 | tags: 16 | - 'v*' 17 | jobs: 18 | goreleaser: 19 | runs-on: ubuntu-latest 20 | steps: 21 | - 22 | name: Checkout 23 | uses: actions/checkout@v2 24 | - 25 | name: Unshallow 26 | run: git fetch --prune --unshallow 27 | - 28 | name: Set up Go 29 | uses: actions/setup-go@v2 30 | with: 31 | go-version: 1.17.7 32 | - 33 | name: Import GPG key 34 | id: import_gpg 35 | uses: paultyng/ghaction-import-gpg@v2.1.0 36 | env: 37 | GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }} 38 | PASSPHRASE: ${{ secrets.PASSPHRASE }} 39 | - 40 | name: Run GoReleaser 41 | uses: goreleaser/goreleaser-action@v2 42 | with: 43 | version: latest 44 | args: release --rm-dist 45 | env: 46 | GPG_FINGERPRINT: ${{ steps.import_gpg.outputs.fingerprint }} 47 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 48 | -------------------------------------------------------------------------------- /.goreleaser.yml: -------------------------------------------------------------------------------- 1 | # Visit https://goreleaser.com for documentation on how to customize this 2 | # behavior. 3 | before: 4 | hooks: 5 | # this is just an example and not a requirement for provider building/publishing 6 | - go mod tidy 7 | builds: 8 | - env: 9 | # goreleaser does not work with CGO, it could also complicate 10 | # usage by users in CI/CD systems like Terraform Cloud where 11 | # they are unable to install libraries. 12 | - CGO_ENABLED=0 13 | mod_timestamp: '{{ .CommitTimestamp }}' 14 | flags: 15 | - -trimpath 16 | ldflags: 17 | - -s -w -X version.ProviderVersion={{.Version}} 18 | goos: 19 | - freebsd 20 | - windows 21 | - linux 22 | - darwin 23 | goarch: 24 | - amd64 25 | - '386' 26 | - arm 27 | - arm64 28 | ignore: 29 | - goos: darwin 30 | goarch: '386' 31 | binary: '{{ .ProjectName }}_v{{ .Version }}' 32 | archives: 33 | - format: zip 34 | name_template: '{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}' 35 | checksum: 36 | name_template: '{{ .ProjectName }}_{{ .Version }}_SHA256SUMS' 37 | algorithm: sha256 38 | signs: 39 | - artifacts: checksum 40 | args: 41 | # if you are using this is a GitHub action or some other automated pipeline, you 42 | # need to pass the batch flag to indicate its not interactive. 43 | - "--batch" 44 | - "--local-user" 45 | - "{{ .Env.GPG_FINGERPRINT }}" # set this environment variable for your signing key 46 | - "--output" 47 | - "${signature}" 48 | - "--detach-sign" 49 | - "${artifact}" 50 | release: 51 | # If you want to manually examine the release before its live, uncomment this line: 52 | # draft: true 53 | changelog: 54 | skip: true 55 | -------------------------------------------------------------------------------- /docs/data-sources/operating_system.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_title: "Equinix Metal: operating_system" 3 | subcategory: "" 4 | description: |- 5 | Get an Equinix Metal operating system image 6 | --- 7 | 8 | # metal_operating_system (Data Source) 9 | 10 | !> **PROVIDER DEPRECATED:** Equinix Metal Provider is now Deprecated. Please consider using [`equinix_metal_operating_system`](https://registry.terraform.io/providers/equinix/equinix/latest/docs/data-sources/equinix_metal_operating_system) data source from the [Equinix provider](https://registry.terraform.io/providers/equinix/equinix/latest/docs) instead of `metal_operating_system`. [See the Metal provider section for more details](../index.md#equinix-metal-provider) on the new provider and available migration guides. 11 | 12 | Use this data source to get Equinix Metal Operating System image. 13 | 14 | ## Example Usage 15 | 16 | ```hcl 17 | data "metal_operating_system" "example" { 18 | distro = "ubuntu" 19 | version = "20.04" 20 | provisionable_on = "c3.medium.x86" 21 | } 22 | 23 | resource "metal_device" "server" { 24 | hostname = "tf.ubuntu" 25 | plan = "c3.medium.x86" 26 | facilities = ["ny5"] 27 | operating_system = data.metal_operating_system.example.id 28 | billing_cycle = "hourly" 29 | project_id = local.project_id 30 | } 31 | ``` 32 | 33 | ## Argument Reference 34 | 35 | * `distro` - (Optional) Name of the OS distribution. 36 | * `name` - (Optional) Name or part of the name of the distribution. Case insensitive. 37 | * `provisionable_on` - (Optional) Plan name. 38 | * `version` - (Optional) Version of the distribution 39 | 40 | ## Attributes Reference 41 | 42 | * `id` - Operating system slug 43 | * `slug` - Operating system slug (same as `id`) 44 | -------------------------------------------------------------------------------- /metal/resource_metal_project_api_key_test.go: -------------------------------------------------------------------------------- 1 | package metal 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" 8 | "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" 9 | "github.com/packethost/packngo" 10 | ) 11 | 12 | func testAccMetalProjectAPIKeyDestroy(s *terraform.State) error { 13 | client := testAccProvider.Meta().(*packngo.Client) 14 | for _, rs := range s.RootModule().Resources { 15 | if rs.Type != "metal_project_api_key" { 16 | continue 17 | } 18 | if _, err := client.APIKeys.ProjectGet(rs.Primary.ID, rs.Primary.Attributes["project_id"], nil); err == nil { 19 | return fmt.Errorf("ProjectAPI key still exists") 20 | } 21 | } 22 | return nil 23 | } 24 | 25 | func testAccMetalProjectAPIKeyConfig_Basic() string { 26 | return fmt.Sprintf(` 27 | 28 | resource "metal_project" "test" { 29 | name = "tfacc-pro-key-test" 30 | } 31 | 32 | resource "metal_project_api_key" "test" { 33 | project_id = metal_project.test.id 34 | description = "tfacc-pro-key" 35 | read_only = true 36 | }`) 37 | } 38 | 39 | func TestAccMetalProjectAPIKey_Basic(t *testing.T) { 40 | resource.ParallelTest(t, resource.TestCase{ 41 | PreCheck: func() { testAccPreCheck(t) }, 42 | Providers: testAccProviders, 43 | CheckDestroy: testAccMetalProjectAPIKeyDestroy, 44 | Steps: []resource.TestStep{ 45 | { 46 | Config: testAccMetalProjectAPIKeyConfig_Basic(), 47 | Check: resource.ComposeTestCheckFunc( 48 | resource.TestCheckResourceAttrSet( 49 | "metal_project_api_key.test", "token"), 50 | resource.TestCheckResourceAttrPair( 51 | "metal_project_api_key.test", "project_id", 52 | "metal_project.test", "id"), 53 | ), 54 | }, 55 | }, 56 | }) 57 | } 58 | -------------------------------------------------------------------------------- /metal/datasource_metal_reserved_ip_block_test.go: -------------------------------------------------------------------------------- 1 | package metal 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" 8 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" 9 | ) 10 | 11 | func testAccDataSourceMetalReservedIPBlockConfig_Basic(name string) string { 12 | return fmt.Sprintf(` 13 | resource "metal_project" "foobar" { 14 | name = "tfacc-pro-reserved_ip_block-%s" 15 | } 16 | 17 | resource "metal_reserved_ip_block" "test" { 18 | project_id = metal_project.foobar.id 19 | metro = "sv" 20 | type = "public_ipv4" 21 | quantity = 2 22 | } 23 | 24 | data "metal_reserved_ip_block" "test" { 25 | project_id = metal_project.foobar.id 26 | ip_address = cidrhost(metal_reserved_ip_block.test.cidr_notation,1) 27 | } 28 | 29 | data "metal_reserved_ip_block" "test_id" { 30 | id = metal_reserved_ip_block.test.id 31 | } 32 | 33 | `, name) 34 | } 35 | 36 | func TestAccDataSourceMetalReservedIPBlock_Basic(t *testing.T) { 37 | 38 | rs := acctest.RandString(10) 39 | 40 | resource.ParallelTest(t, resource.TestCase{ 41 | PreCheck: func() { testAccPreCheck(t) }, 42 | Providers: testAccProviders, 43 | CheckDestroy: testAccCheckMetalReservedIPBlockDestroy, 44 | Steps: []resource.TestStep{ 45 | { 46 | Config: testAccDataSourceMetalReservedIPBlockConfig_Basic(rs), 47 | Check: resource.ComposeTestCheckFunc( 48 | resource.TestCheckResourceAttrPair( 49 | "metal_reserved_ip_block.test", "id", 50 | "data.metal_reserved_ip_block.test", "id", 51 | ), 52 | resource.TestCheckResourceAttrPair( 53 | "metal_reserved_ip_block.test", "cidr_notation", 54 | "data.metal_reserved_ip_block.test_id", "cidr_notation", 55 | ), 56 | ), 57 | }, 58 | }, 59 | }) 60 | } 61 | -------------------------------------------------------------------------------- /docs/resources/project_api_key.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_title: "Equinix Metal: Metal Project API Key" 3 | subcategory: "" 4 | description: |- 5 | Create Equinix Metal Project API Keys 6 | --- 7 | 8 | # metal_project_api_key (Resource) 9 | 10 | !> **PROVIDER DEPRECATED:** Equinix Metal Provider is now Deprecated. Please consider using [`equinix_metal_project_api_key`](https://registry.terraform.io/providers/equinix/equinix/latest/docs/resources/equinix_metal_project_api_key) resource from the [Equinix provider](https://registry.terraform.io/providers/equinix/equinix/latest/docs) instead of `metal_project_api_key`. [See the Metal provider section for more details](../index.md#equinix-metal-provider) on the new provider and available migration guides. 11 | 12 | Use this resource to create Metal Project API Key resources in Equinix Metal. Project API keys can be used to create and read resources in a single project. Each API key contains a token which can be used for authentication in Equinix Metal HTTP API (in HTTP request header `X-Auth-Token`). 13 | 14 | Read-only keys only allow to list and view existing resources, read-write keys can also be used to create resources. 15 | 16 | ## Example Usage 17 | 18 | ```hcl 19 | 20 | # Create a new read-only API key in existing project 21 | 22 | resource "metal_project_api_key" "test" { 23 | project_id = local.existing_project_id 24 | description = "Read-only key scoped to a projct" 25 | read_only = true 26 | } 27 | ``` 28 | 29 | ## Argument Reference 30 | 31 | * `project_id` - UUID of the project where the API key is scoped to 32 | * `description` - Description string for the Project API Key resource 33 | * `read-only` - Flag indicating whether the API key shoud be read-only 34 | 35 | ## Attributes Reference 36 | 37 | * `token` - API token which can be used in Equinix Metal API clients 38 | -------------------------------------------------------------------------------- /metal/datasource_metal_vrf.go: -------------------------------------------------------------------------------- 1 | package metal 2 | 3 | import ( 4 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" 5 | ) 6 | 7 | func dataSourceMetalVRF() *schema.Resource { 8 | return &schema.Resource{ 9 | DeprecationMessage: deprecatedProviderMsg, 10 | Read: dataSourceMetalVRFRead, 11 | Schema: map[string]*schema.Schema{ 12 | "vrf_id": { 13 | Required: true, 14 | Type: schema.TypeString, 15 | Description: "ID of the VRF to lookup", 16 | }, 17 | "name": { 18 | Type: schema.TypeString, 19 | Computed: true, 20 | Description: "User-supplied name of the VRF, unique to the project", 21 | }, 22 | "description": { 23 | Type: schema.TypeString, 24 | Computed: true, 25 | Description: "Description of the VRF", 26 | }, 27 | "metro": { 28 | Type: schema.TypeString, 29 | Computed: true, 30 | Description: "Metro Code", 31 | }, 32 | "local_asn": { 33 | Type: schema.TypeInt, 34 | Computed: true, 35 | Description: "The 4-byte ASN set on the VRF.", 36 | }, 37 | "ip_ranges": { 38 | Type: schema.TypeSet, 39 | Computed: true, 40 | Description: "All IPv4 and IPv6 Ranges that will be available to BGP Peers. IPv4 addresses must be /8 or smaller with a minimum size of /29. IPv6 must be /56 or smaller with a minimum size of /64. Ranges must not overlap other ranges within the VRF.", 41 | Elem: &schema.Schema{Type: schema.TypeString}, 42 | }, 43 | "project_id": { 44 | Type: schema.TypeString, 45 | Computed: true, 46 | Description: "Project ID", 47 | }, 48 | }, 49 | } 50 | } 51 | 52 | func dataSourceMetalVRFRead(d *schema.ResourceData, meta interface{}) error { 53 | vrfId, _ := d.Get("vrf_id").(string) 54 | 55 | d.SetId(vrfId) 56 | return resourceMetalVRFRead(d, meta) 57 | } 58 | -------------------------------------------------------------------------------- /docs/data-sources/gateway.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_title: "Equinix Metal: Metal Gateway" 3 | subcategory: "" 4 | description: |- 5 | Retrieve Equinix Metal Gateways 6 | --- 7 | 8 | # metal_gateway (Data Source) 9 | 10 | !> **PROVIDER DEPRECATED:** Equinix Metal Provider is now Deprecated. Please consider using [`equinix_metal_gateway`](https://registry.terraform.io/providers/equinix/equinix/latest/docs/data-sources/equinix_metal_gateway) data source from the [Equinix provider](https://registry.terraform.io/providers/equinix/equinix/latest/docs) instead of `metal_gateway`. [See the Metal provider section for more details](../index.md#equinix-metal-provider) on the new provider and available migration guides. 11 | 12 | Use this datasource to retrieve Metal Gateway resources in Equinix Metal. 13 | 14 | ~> VRF features are not generally available. The interfaces related to VRF resources may change ahead of general availability. 15 | 16 | ## Example Usage 17 | 18 | ```hcl 19 | # Create Metal Gateway for a VLAN with a private IPv4 block with 8 IP addresses 20 | 21 | resource "metal_vlan" "test" { 22 | description = "test VLAN in SV" 23 | metro = "sv" 24 | project_id = local.project_id 25 | } 26 | 27 | data "metal_gateway" "test" { 28 | gateway_id = local.gateway_id 29 | } 30 | ``` 31 | 32 | ## Argument Reference 33 | 34 | * `gateway_id` - (Required) UUID of the metal gateway resource to retrieve 35 | 36 | ## Attributes Reference 37 | 38 | * `project_id` - UUID of the project where the gateway is scoped to 39 | * `vlan_id` - UUID of the VLAN where the gateway is scoped to 40 | * `ip_reservation_id` - UUID of IP reservation block bound to the gateway 41 | * `private_ipv4_subnet_size` - Size of the private IPv4 subnet bound to this metal gateway, one of (8, 16, 32, 64, 128)` 42 | * `state` - Status of the gateway resource 43 | * `vrf_id` - UUID of the VRF associated with the IP Reservation. 44 | -------------------------------------------------------------------------------- /docs/data-sources/vrf.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_title: "Equinix Metal: metal_vrf" 3 | subcategory: "" 4 | description: |- 5 | (not-GA) Provides a datasource for Equinix Metal VRF. 6 | --- 7 | 8 | # metal_virtual_circuit (Data Source) 9 | 10 | !> **PROVIDER DEPRECATED:** Equinix Metal Provider is now Deprecated. Please consider using [`equinix_metal_virtual_circuit`](https://registry.terraform.io/providers/equinix/equinix/latest/docs/data-sources/equinix_metal_virtual_circuit) data source from the [Equinix provider](https://registry.terraform.io/providers/equinix/equinix/latest/docs) instead of `metal_virtual_circuit`. [See the Metal provider section for more details](../index.md#equinix-metal-provider) on the new provider and available migration guides. 11 | 12 | Use this data source to retrieve a VRF resource. 13 | 14 | ~> VRF features are not generally available. The interfaces related to VRF resources may change ahead of general availability. 15 | 16 | ## Example Usage 17 | 18 | ```hcl 19 | data "metal_vrf" "example_vrf" { 20 | vrf_id = "48630899-9ff2-4ce6-a93f-50ff4ebcdf6e" 21 | } 22 | ``` 23 | 24 | ## Argument Reference 25 | 26 | The following arguments are supported: 27 | 28 | * `vrf_id` - (Required) ID of the VRF resource 29 | 30 | ## Attributes Reference 31 | 32 | In addition to all arguments above, the following attributes are exported: 33 | 34 | * `name` - User-supplied name of the VRF, unique to the project 35 | * `metro` - Metro ID or Code where the VRF will be deployed. 36 | * `project_id` - Project ID where the VRF will be deployed. 37 | * `description` - Description of the VRF. 38 | * `local_asn` - The 4-byte ASN set on the VRF. 39 | * `ip_ranges` - All IPv4 and IPv6 Ranges that will be available to BGP Peers. IPv4 addresses must be /8 or smaller with a minimum size of /29. IPv6 must be /56 or smaller with a minimum size of /64. Ranges must not overlap other ranges within the VRF. 40 | -------------------------------------------------------------------------------- /metal/datasource_metal_gateway.go: -------------------------------------------------------------------------------- 1 | package metal 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" 7 | ) 8 | 9 | func dataSourceMetalGateway() *schema.Resource { 10 | return &schema.Resource{ 11 | DeprecationMessage: deprecatedProviderMsg, 12 | Read: dataSourceMetalGatewayRead, 13 | Schema: map[string]*schema.Schema{ 14 | "gateway_id": { 15 | Required: true, 16 | Type: schema.TypeString, 17 | Description: "UUID of the Metal Gateway to fetch", 18 | }, 19 | "project_id": { 20 | Computed: true, 21 | Type: schema.TypeString, 22 | Description: "UUID of the Project where the Gateway is scoped to", 23 | }, 24 | "vlan_id": { 25 | Computed: true, 26 | Type: schema.TypeString, 27 | Description: "UUID of the VLAN to associate", 28 | }, 29 | "vrf_id": { 30 | Computed: true, 31 | Type: schema.TypeString, 32 | Description: "UUID of the VRF associated with the IP Reservation", 33 | }, 34 | "ip_reservation_id": { 35 | Computed: true, 36 | Type: schema.TypeString, 37 | Description: "UUID of the IP Reservation to associate, must be in the same metro as the VLAN", 38 | }, 39 | "private_ipv4_subnet_size": { 40 | Computed: true, 41 | Type: schema.TypeInt, 42 | Description: fmt.Sprintf("Size of the private IPv4 subnet to create for this gateway, one of %v", subnetSizes), 43 | }, 44 | "state": { 45 | Type: schema.TypeString, 46 | Computed: true, 47 | Description: "Status of the virtual circuit resource", 48 | }, 49 | }, 50 | } 51 | } 52 | 53 | func dataSourceMetalGatewayRead(d *schema.ResourceData, meta interface{}) error { 54 | gatewayId, _ := d.Get("gateway_id").(string) 55 | 56 | d.SetId(gatewayId) 57 | return resourceMetalGatewayRead(d, meta) 58 | } 59 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Equinix Metal Terraform Provider 2 | 3 | [![End of Life](https://img.shields.io/badge/Stability-EndOfLife-black.svg)](end-of-life-statement.md#end-of-life-statements) 4 | [![GitHub release](https://img.shields.io/github/release/equinix/terraform-provider-metal/all.svg?style=flat-square)](https://github.com/equinix/terraform-provider-metal/releases) 5 | [![Go Report Card](https://goreportcard.com/badge/github.com/equinix/terraform-provider-metal)](https://goreportcard.com/report/github.com/equinix/terraform-provider-metal) 6 | 7 | [![Slack](https://slack.equinixmetal.com/badge.svg)](https://slack.equinixmetal.com) 8 | [![Twitter Follow](https://img.shields.io/twitter/follow/equinixmetal.svg?style=social&label=Follow)](https://twitter.com/intent/follow?screen_name=equinixmetal) 9 | 10 | 11 | 12 | This repository is now [End of Life](https://github.com/equinix-labs/equinix-labs/blob/main/end-of-life-statement.md) meaning that this software is no longer supported nor maintained by Equinix Metal or its community. 13 | 14 | [Please review the Metal to Equinix provider migration guide](https://registry.terraform.io/providers/equinix/equinix/latest/docs/guides/migration_guide_equinix_metal). A guide is also available for [migrating from the Packet provider](https://registry.terraform.io/providers/equinix/equinix/latest/docs/guides/migration_guide_packet). 15 | 16 | The [Equinix provider](https://registry.terraform.io/providers/equinix/equinix/latest/docs) has full support for existing Terraform managed Metal resources once Terraform configuration and state are adapted. The Equinix provider manages resources including Network Edge and Fabric in addition to Metal. 17 | 18 | See for documentation on the resources included in this deprecated provider. 19 | -------------------------------------------------------------------------------- /docs/data-sources/organization.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_title: "Equinix Metal: metal_organization" 3 | subcategory: "" 4 | description: |- 5 | Provides an Equinix Metal Organization datasource. This can be used to read existing Organizations. 6 | --- 7 | 8 | # metal_organization (Data Source) 9 | 10 | !> **PROVIDER DEPRECATED:** Equinix Metal Provider is now Deprecated. Please consider using [`equinix_metal_organization`](https://registry.terraform.io/providers/equinix/equinix/latest/docs/data-sources/equinix_metal_organization) data source from the [Equinix provider](https://registry.terraform.io/providers/equinix/equinix/latest/docs) instead of `metal_organization`. [See the Metal provider section for more details](../index.md#equinix-metal-provider) on the new provider and available migration guides. 11 | 12 | Provides an Equinix Metal organization datasource. 13 | 14 | ## Example Usage 15 | 16 | ```hcl 17 | # Fetch a organization data and show projects which belong to it 18 | data "metal_organization" "test" { 19 | organization_id = local.org_id 20 | } 21 | 22 | output "projects_in_the_org" { 23 | value = data.metal_organization.test.project_ids 24 | } 25 | ``` 26 | 27 | ## Argument Reference 28 | 29 | The following arguments are supported: 30 | 31 | * `name` - The organization name 32 | * `organization_id` - The UUID of the organization resource 33 | 34 | Exactly one of `name` or `organization_id` must be given. 35 | 36 | ## Attributes Reference 37 | 38 | The following attributes are exported: 39 | 40 | * `project_ids` - UUIDs of project resources which belong to this organization 41 | * `description` - Description string 42 | * `website` - Website link 43 | * `twitter` - Twitter handle 44 | * `logo` - Logo URL 45 | * `address` - Address information 46 | * `address` - Postal address. 47 | * `city` - City name. 48 | * `country` - Two letter country code (ISO 3166-1 alpha-2), e.g. US. 49 | * `zip_code` - Zip Code. 50 | * `state` - State name. 51 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Hi there, 2 | 3 | Thank you for opening an issue. Please note that we try to keep the Terraform issue tracker reserved for bug reports and feature requests. For general usage questions, please see: https://www.terraform.io/community.html. 4 | 5 | ### Terraform Version 6 | Run `terraform -v` to show the version. If you are not running the latest version of Terraform, please upgrade because your issue may have already been fixed. 7 | 8 | ### Affected Resource(s) 9 | Please list the resources as a list, for example: 10 | - metal_device 11 | - metal_spot_market_request 12 | 13 | If this issue appears to affect multiple resources, it may be an issue with Terraform's core, so please mention this. 14 | 15 | ### Terraform Configuration Files 16 | ```hcl 17 | # Copy-paste your Terraform configurations here - for large Terraform configs, 18 | # please use a service like Dropbox and share a link to the ZIP file. 19 | ``` 20 | 21 | ### Debug Output 22 | Please provider a link to a GitHub Gist containing the complete debug output: https://www.terraform.io/docs/internals/debugging.html. Please do NOT paste the debug output in the issue; just paste a link to the Gist. Note: the debug log will contain your API key, please search the log for `X-Auth-Token` (HTTP request header holding the API token) and remove it. 23 | 24 | ### Panic Output 25 | If Terraform produced a panic, please provide a link to a GitHub Gist containing the output of the `crash.log`. 26 | 27 | ### Expected Behavior 28 | What should have happened? 29 | 30 | ### Actual Behavior 31 | What actually happened? 32 | 33 | ### Steps to Reproduce 34 | Please list the steps required to reproduce the issue, for example: 35 | 1. `terraform apply` 36 | 37 | ### Important Factoids 38 | Are there anything atypical about your accounts that we should know? 39 | 40 | ### References 41 | Are there any other GitHub issues (open or closed) or Pull Requests that should be linked here? For example: 42 | - #1234 43 | -------------------------------------------------------------------------------- /docs/data-sources/metro.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_title: "Equinix Metal: metal_metro" 3 | subcategory: "" 4 | description: |- 5 | Provides an Equinix Metal metro datasource. This can be used to read metros. 6 | --- 7 | 8 | # metal_metro (Data Source) 9 | 10 | !> **PROVIDER DEPRECATED:** Equinix Metal Provider is now Deprecated. Please consider using [`equinix_metal_metro`](https://registry.terraform.io/providers/equinix/equinix/latest/docs/data-sources/equinix_metal_metro) data source from the [Equinix provider](https://registry.terraform.io/providers/equinix/equinix/latest/docs) instead of `metal_metro`. [See the Metal provider section for more details](../index.md#equinix-metal-provider) on the new provider and available migration guides. 11 | 12 | Provides an Equinix Metal metro datasource. 13 | 14 | ## Example Usage 15 | 16 | ```hcl 17 | # Fetch a metro by code and show its ID 18 | 19 | data "metal_metro" "sv" { 20 | code = "sv" 21 | } 22 | 23 | output "id" { 24 | value = data.metal_metro.sv.id 25 | } 26 | ``` 27 | 28 | 29 | ```hcl 30 | # Verify that metro "sv" has capacity for provisioning 2 c3.small.x86 31 | devices and 1 c3.medium.x86 device 32 | 33 | data "metal_facility" "test" { 34 | code = "dc13" 35 | capacity { 36 | plan = "c3.small.x86" 37 | quantity = 2 38 | } 39 | capacity { 40 | plan = "c3.medium.x86" 41 | quantity = 1 42 | } 43 | } 44 | 45 | ``` 46 | 47 | ## Argument Reference 48 | 49 | The following arguments are supported: 50 | 51 | * `code` - The metro code 52 | 53 | Metros can be looked up by `code`. 54 | 55 | ## Attributes Reference 56 | 57 | The following attributes are exported: 58 | 59 | * `id` - The ID of the metro 60 | * `code` - The code of the metro 61 | * `country` - The country of the metro 62 | * `name` - The name of the metro 63 | * `capacity` - (Optional) Ensure that queried metro has capacity for specified number of given plans 64 | - `plan` - device plan to check 65 | - `quantity` - number of device to check 66 | -------------------------------------------------------------------------------- /docs/data-sources/project_ssh_key.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_title: "Equinix Metal: metal_project_ssh_key" 3 | subcategory: "" 4 | description: |- 5 | Provides an Equinix Metal Project SSH Key datasource. 6 | --- 7 | 8 | # metal_project_ssh_key (Data Source) 9 | 10 | !> **PROVIDER DEPRECATED:** Equinix Metal Provider is now Deprecated. Please consider using [`equinix_metal_project_ssh_key`](https://registry.terraform.io/providers/equinix/equinix/latest/docs/data-sources/equinix_metal_project_ssh_key) data source from the [Equinix provider](https://registry.terraform.io/providers/equinix/equinix/latest/docs) instead of `metal_project_ssh_key`. [See the Metal provider section for more details](../index.md#equinix-metal-provider) on the new provider and available migration guides. 11 | 12 | Use this datasource to retrieve attributes of a Project SSH Key API resource. 13 | 14 | ## Example Usage 15 | 16 | ```hcl 17 | # Get Project SSH Key by name 18 | data "metal_project_ssh_key" "my_key" { 19 | search = "username@hostname" 20 | project_id = local.project_id 21 | } 22 | ``` 23 | 24 | ## Argument Reference 25 | 26 | The following arguments are supported: 27 | 28 | * `search` - (Optional) The name, fingerprint, or public_key of the SSH Key to search for 29 | in the Equinix Metal project 30 | * `id` - (Optional) The id of the SSH Key to search for in the Equinix Metal project 31 | * `project_id` - The Equinix Metal project id of the Equinix Metal SSH Key 32 | 33 | One of either `search` or `id` must be provided along with `project_id`. 34 | 35 | ## Attributes Reference 36 | 37 | The following attributes are exported: 38 | 39 | * `id` - The unique ID of the key 40 | * `name` - The name of the SSH key 41 | * `public_key` - The text of the public key 42 | * `project_id` - The ID of parent project 43 | * `owner_id` - The ID of parent project (same as project_id) 44 | * `fingerprint` - The fingerprint of the SSH key 45 | * `created` - The timestamp for when the SSH key was created 46 | * `updated` - The timestamp for the last time the SSH key was updated 47 | -------------------------------------------------------------------------------- /docs/resources/vlan.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_title: "Equinix Metal: metal_vlan" 3 | subcategory: "" 4 | description: |- 5 | Provides a resource for Equinix Metal Virtual Network. 6 | --- 7 | 8 | # metal_vlan (Resource) 9 | 10 | !> **PROVIDER DEPRECATED:** Equinix Metal Provider is now Deprecated. Please consider using [`equinix_metal_vlan`](https://registry.terraform.io/providers/equinix/equinix/latest/docs/resources/equinix_metal_vlan) resource from the [Equinix provider](https://registry.terraform.io/providers/equinix/equinix/latest/docs) instead of `metal_vlan`. [See the Metal provider section for more details](../index.md#equinix-metal-provider) on the new provider and available migration guides. 11 | 12 | Provides a resource to allow users to manage Virtual Networks in their projects. 13 | 14 | To learn more about Layer 2 networking in Equinix Metal, refer to 15 | 16 | * 17 | * 18 | 19 | ## Example Usage 20 | 21 | ```hcl 22 | # Create a new VLAN in facility "sv15" 23 | resource "metal_vlan" "vlan1" { 24 | description = "VLAN in New Jersey" 25 | facility = "sv15" 26 | project_id = local.project_id 27 | } 28 | 29 | # Create a new VLAN in metro "esv" 30 | resource "metal_vlan" "vlan1" { 31 | description = "VLAN in New Jersey" 32 | metro = "sv" 33 | project_id = local.project_id 34 | vxlan = 1040 35 | } 36 | ``` 37 | 38 | ## Argument Reference 39 | 40 | The following arguments are supported: 41 | 42 | * `project_id` - (Required) ID of parent project 43 | * `facility` - (Required) Facility where to create the VLAN 44 | * `description` - Description string 45 | * `vxlan` - VLAN ID, must be unique in metro 46 | 47 | ## Attributes Reference 48 | 49 | The following attributes are exported: 50 | 51 | * `vxlan` - VXLAN segment ID 52 | 53 | ## Import 54 | 55 | This resource can be imported using an existing VLAN ID (UUID): 56 | 57 | ```sh 58 | terraform import metal_vlan {existing_vlan_id} 59 | ``` 60 | -------------------------------------------------------------------------------- /metal/datasource_metal_operating_system_test.go: -------------------------------------------------------------------------------- 1 | package metal 2 | 3 | import ( 4 | "regexp" 5 | "testing" 6 | 7 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" 8 | ) 9 | 10 | func TestAccMetalOperatingSystem_Basic(t *testing.T) { 11 | 12 | resource.ParallelTest(t, resource.TestCase{ 13 | PreCheck: func() { testAccPreCheck(t) }, 14 | Providers: testAccProviders, 15 | Steps: []resource.TestStep{ 16 | {Config: testOperatingSystemConfig_Basic, 17 | Check: resource.ComposeTestCheckFunc( 18 | resource.TestCheckResourceAttr("data.metal_operating_system.example", "slug", "ubuntu_20_04"), 19 | ), 20 | }, 21 | }, 22 | }) 23 | } 24 | 25 | const testOperatingSystemConfig_Basic = ` 26 | data "metal_operating_system" "example" { 27 | distro = "ubuntu" 28 | version = "20.04" 29 | }` 30 | 31 | var matchErrOSNotFound = regexp.MustCompile(".*There are no operating systems*") 32 | 33 | func TestAccMetalOperatingSystem_NotFound(t *testing.T) { 34 | 35 | resource.ParallelTest(t, resource.TestCase{ 36 | PreCheck: func() { testAccPreCheck(t) }, 37 | Providers: testAccProviders, 38 | Steps: []resource.TestStep{ 39 | {Config: testOperatingSystemConfig_NotFound, 40 | ExpectError: matchErrOSNotFound, 41 | }, 42 | }, 43 | }) 44 | } 45 | 46 | const testOperatingSystemConfig_NotFound = ` 47 | data "metal_operating_system" "example" { 48 | distro = "NOTEXISTS" 49 | version = "alpha" 50 | }` 51 | 52 | var matchErrOSAmbiguous = regexp.MustCompile(".*There is more than one operating system.*") 53 | 54 | func TestAccMetalOperatingSystem_Ambiguous(t *testing.T) { 55 | 56 | resource.ParallelTest(t, resource.TestCase{ 57 | PreCheck: func() { testAccPreCheck(t) }, 58 | Providers: testAccProviders, 59 | Steps: []resource.TestStep{ 60 | {Config: testOperatingSystemConfig_Ambiguous, 61 | ExpectError: matchErrOSAmbiguous, 62 | }, 63 | }, 64 | }) 65 | } 66 | 67 | const testOperatingSystemConfig_Ambiguous = ` 68 | data "metal_operating_system" "example" { 69 | distro = "ubuntu" 70 | }` 71 | -------------------------------------------------------------------------------- /metal/datasource_metal_ip_block_ranges_test.go: -------------------------------------------------------------------------------- 1 | package metal 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" 8 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" 9 | ) 10 | 11 | func TestAccMetalIPBlockRanges_Basic(t *testing.T) { 12 | 13 | rs := acctest.RandString(10) 14 | 15 | resource.ParallelTest(t, resource.TestCase{ 16 | PreCheck: func() { testAccPreCheck(t) }, 17 | Providers: testAccProviders, 18 | Steps: []resource.TestStep{ 19 | { 20 | Config: testIPBlockRangesConfig_Basic(rs), 21 | Check: resource.ComposeTestCheckFunc( 22 | resource.TestCheckResourceAttrSet( 23 | "data.metal_ip_block_ranges.test", "ipv6.0"), 24 | resource.TestCheckResourceAttrPair( 25 | "metal_ip_attachment.test", "device_id", 26 | "metal_device.test", "id"), 27 | ), 28 | }, 29 | { 30 | ResourceName: "metal_ip_attachment.test", 31 | ImportState: true, 32 | ImportStateVerify: true, 33 | }, 34 | }, 35 | }) 36 | } 37 | 38 | func testIPBlockRangesConfig_Basic(name string) string { 39 | return fmt.Sprintf(` 40 | %s 41 | 42 | resource "metal_project" "test" { 43 | name = "tfacc-pro-precreated_ip_block-%s" 44 | } 45 | 46 | resource "metal_device" "test" { 47 | hostname = "tfacc-device-test-ip" 48 | plan = local.plan 49 | facilities = local.facilities 50 | operating_system = "ubuntu_16_04" 51 | billing_cycle = "hourly" 52 | project_id = metal_project.test.id 53 | 54 | lifecycle { 55 | ignore_changes = [ 56 | plan, 57 | facilities, 58 | ] 59 | } 60 | } 61 | 62 | data "metal_ip_block_ranges" "test" { 63 | facility = metal_device.test.deployed_facility 64 | project_id = metal_device.test.project_id 65 | } 66 | 67 | resource "metal_ip_attachment" "test" { 68 | device_id = metal_device.test.id 69 | cidr_notation = cidrsubnet(data.metal_ip_block_ranges.test.ipv6.0, 8,2) 70 | }`, confAccMetalDevice_base(preferable_plans, preferable_metros), name) 71 | } 72 | -------------------------------------------------------------------------------- /docs/data-sources/facility.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_title: "Equinix Metal: metal_facility" 3 | subcategory: "" 4 | description: |- 5 | Provides an Equinix Metal facility datasource. This can be used to read facilities. 6 | --- 7 | 8 | # metal_facility (Data Source) 9 | 10 | !> **PROVIDER DEPRECATED:** Equinix Metal Provider is now Deprecated. Please consider using [`equinix_metal_facility`](https://registry.terraform.io/providers/equinix/equinix/latest/docs/data-sources/equinix_metal_facility) data source from the [Equinix provider](https://registry.terraform.io/providers/equinix/equinix/latest/docs) instead of `metal_facility`. [See the Metal provider section for more details](../index.md#equinix-metal-provider) on the new provider and available migration guides. 11 | 12 | Provides an Equinix Metal facility datasource. 13 | 14 | ## Example Usage 15 | 16 | ```hcl 17 | # Fetch a facility by code and show its ID 18 | 19 | data "metal_facility" "ny5" { 20 | code = "ny5" 21 | } 22 | 23 | output "id" { 24 | value = data.metal_facility.ny5.id 25 | } 26 | ``` 27 | 28 | ```hcl 29 | # Verify that facility "dc13" has capacity for provisioning 2 c3.small.x86 30 | devices and 1 c3.medium.x86 device 31 | 32 | data "metal_facility" "test" { 33 | code = "dc13" 34 | capacity { 35 | plan = "c3.small.x86" 36 | quantity = 2 37 | } 38 | capacity { 39 | plan = "c3.medium.x86" 40 | quantity = 1 41 | } 42 | } 43 | 44 | ``` 45 | 46 | ## Argument Reference 47 | 48 | The following arguments are supported: 49 | 50 | * `code` - The facility code 51 | * `features_required` - Set of feature strings that the facility must have 52 | 53 | Facilities can be looked up by `code`. 54 | 55 | ## Attributes Reference 56 | 57 | The following attributes are exported: 58 | 59 | * `id` - The ID of the facility 60 | * `name` - The name of the facility 61 | * `features` - The features of the facility 62 | * `metro` - The metro code the facility is part of 63 | * `capacity` - (Optional) Ensure that queried facility has capacity for specified number of given plans 64 | - `plan` - device plan to check 65 | - `quantity` - number of device to check 66 | 67 | -------------------------------------------------------------------------------- /metal/datasource_metal_precreated_ip_block_test.go: -------------------------------------------------------------------------------- 1 | package metal 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" 8 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" 9 | ) 10 | 11 | func TestAccMetalDatasourcePreCreatedIPBlock_Basic(t *testing.T) { 12 | 13 | rs := acctest.RandString(10) 14 | 15 | resource.ParallelTest(t, resource.TestCase{ 16 | PreCheck: func() { testAccPreCheck(t) }, 17 | Providers: testAccProviders, 18 | Steps: []resource.TestStep{ 19 | { 20 | Config: testDatasourcePreCreatedIPBlockConfig_Basic(rs), 21 | Check: resource.ComposeTestCheckFunc( 22 | resource.TestCheckResourceAttrSet( 23 | "data.metal_precreated_ip_block.test", "cidr_notation"), 24 | resource.TestCheckResourceAttrPair( 25 | "metal_ip_attachment.test", "device_id", 26 | "metal_device.test", "id"), 27 | ), 28 | }, 29 | { 30 | ResourceName: "metal_ip_attachment.test", 31 | ImportState: true, 32 | ImportStateVerify: true, 33 | }, 34 | }, 35 | }) 36 | } 37 | 38 | func testDatasourcePreCreatedIPBlockConfig_Basic(name string) string { 39 | return fmt.Sprintf(` 40 | %s 41 | 42 | resource "metal_project" "test" { 43 | name = "tfacc-pro-recreated_ip_block-%s" 44 | } 45 | 46 | resource "metal_device" "test" { 47 | hostname = "tfacc-device-test-ip-blockt" 48 | plan = local.plan 49 | metro = local.metro 50 | operating_system = "ubuntu_16_04" 51 | billing_cycle = "hourly" 52 | project_id = metal_project.test.id 53 | 54 | lifecycle { 55 | ignore_changes = [ 56 | plan, 57 | metro, 58 | ] 59 | } 60 | } 61 | 62 | data "metal_precreated_ip_block" "test" { 63 | facility = metal_device.test.deployed_facility 64 | project_id = metal_device.test.project_id 65 | address_family = 6 66 | public = true 67 | } 68 | 69 | resource "metal_ip_attachment" "test" { 70 | device_id = metal_device.test.id 71 | cidr_notation = cidrsubnet(data.metal_precreated_ip_block.test.cidr_notation,8,2) 72 | } 73 | `, confAccMetalDevice_base(preferable_plans, preferable_metros), name) 74 | } 75 | -------------------------------------------------------------------------------- /docs/resources/organization.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_title: "Equinix Metal: metal_organization" 3 | subcategory: "" 4 | description: |- 5 | Provides an Equinix Metal Organization resource. 6 | --- 7 | 8 | # metal_organization (Resource) 9 | 10 | !> **PROVIDER DEPRECATED:** Equinix Metal Provider is now Deprecated. Please consider using [`equinix_metal_organization`](https://registry.terraform.io/providers/equinix/equinix/latest/docs/resources/equinix_metal_organization) resource from the [Equinix provider](https://registry.terraform.io/providers/equinix/equinix/latest/docs) instead of `metal_organization`. [See the Metal provider section for more details](../index.md#equinix-metal-provider) on the new provider and available migration guides. 11 | 12 | Provides a resource to manage organization resource in Equinix Metal. 13 | 14 | ## Example Usage 15 | 16 | ```hcl 17 | # Create a new Project 18 | resource "metal_organization" "tf_organization_1" { 19 | name = "foobar" 20 | description = "quux" 21 | } 22 | ``` 23 | 24 | ## Argument Reference 25 | 26 | The following arguments are supported: 27 | 28 | * `name` - (Required) The name of the Organization 29 | * `address` - (Required) An object that has the address information. See [Address](#address) 30 | below for more details. 31 | * `description` - (Optional) Description string 32 | * `website` - (Optional) Website link 33 | * `twitter` - (Optional) Twitter handle 34 | * `logo` - (Optional) Logo URL 35 | 36 | ### Address 37 | 38 | The `address` block contains: 39 | 40 | * `address` - (Required) Postal address. 41 | * `city` - (Required) City name. 42 | * `country` - (Required) Two letter country code (ISO 3166-1 alpha-2), e.g. US. 43 | * `zip_code` - (Required) Zip Code. 44 | * `state` - (Optional) State name. 45 | 46 | ## Attributes Reference 47 | 48 | The following attributes are exported: 49 | 50 | * `id` - The unique ID of the organization 51 | * `name` - The name of the Organization 52 | * `description` - Description string 53 | * `website` - Website link 54 | * `twitter` - Twitter handle 55 | * `logo` - Logo URL 56 | 57 | ## Import 58 | 59 | This resource can be imported using an existing organization ID: 60 | 61 | ```sh 62 | terraform import metal_organization {existing_organization_id} 63 | ``` 64 | -------------------------------------------------------------------------------- /metal/datasource_metal_metro.go: -------------------------------------------------------------------------------- 1 | package metal 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" 7 | "github.com/packethost/packngo" 8 | ) 9 | 10 | func dataSourceMetalMetro() *schema.Resource { 11 | return &schema.Resource{ 12 | DeprecationMessage: deprecatedProviderMsg, 13 | Read: dataSourceMetalMetroRead, 14 | Schema: map[string]*schema.Schema{ 15 | "id": { 16 | Type: schema.TypeString, 17 | Description: "The ID of this Metro.", 18 | Computed: true, 19 | }, 20 | "code": { 21 | Type: schema.TypeString, 22 | Description: "The code of the Metro to match", 23 | Required: true, 24 | }, 25 | "country": { 26 | Type: schema.TypeString, 27 | Description: "The country of this Metro.", 28 | Computed: true, 29 | }, 30 | "name": { 31 | Type: schema.TypeString, 32 | Description: "The name of this Metro.", 33 | Computed: true, 34 | }, 35 | "capacity": capacitySchema(), 36 | }, 37 | } 38 | } 39 | 40 | func dataSourceMetalMetroRead(d *schema.ResourceData, meta interface{}) error { 41 | client := meta.(*packngo.Client) 42 | code := d.Get("code").(string) 43 | 44 | _, capacityOk := d.GetOk("capacity") 45 | if capacityOk { 46 | ci := getCapacityInput( 47 | d.Get("capacity").([]interface{}), 48 | packngo.ServerInfo{Metro: code}, 49 | ) 50 | res, _, err := client.CapacityService.CheckMetros(ci) 51 | if err != nil { 52 | return err 53 | } 54 | for _, s := range res.Servers { 55 | if !s.Available { 56 | return fmt.Errorf("Not enough capacity in metro %s for %d device(s) of plan %s", s.Facility, s.Quantity, s.Plan) 57 | } 58 | } 59 | if err != nil { 60 | return err 61 | } 62 | } 63 | 64 | metros, _, err := client.Metros.List(nil) 65 | if err != nil { 66 | return fmt.Errorf("Error listing Metros: %s", err) 67 | } 68 | 69 | for _, m := range metros { 70 | if m.Code == code { 71 | d.SetId(m.ID) 72 | return setMap(d, map[string]interface{}{ 73 | "id": m.ID, 74 | "code": m.Code, 75 | "name": m.Name, 76 | "country": m.Country, 77 | }) 78 | } 79 | } 80 | 81 | return fmt.Errorf("Metro %s was not found", code) 82 | } 83 | -------------------------------------------------------------------------------- /docs/data-sources/port.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_title: "Equinix Metal: precreated_port" 3 | subcategory: "" 4 | description: |- 5 | Fetch device ports 6 | --- 7 | 8 | # metal_port (Data Source) 9 | 10 | !> **PROVIDER DEPRECATED:** Equinix Metal Provider is now Deprecated. Please consider using [`equinix_metal_port`](https://registry.terraform.io/providers/equinix/equinix/latest/docs/data-sources/equinix_metal_port) data source from the [Equinix provider](https://registry.terraform.io/providers/equinix/equinix/latest/docs) instead of `metal_port`. [See the Metal provider section for more details](../index.md#equinix-metal-provider) on the new provider and available migration guides. 11 | 12 | Use this data source to read ports of existing devices. You can read port by either its UUID, or by a device UUID and port name. 13 | 14 | ## Example Usage 15 | 16 | Create a device and read it's eth0 port to the datasource. 17 | 18 | ```hcl 19 | locals { 20 | project_id = "" 21 | } 22 | 23 | resource "metal_device" "test" { 24 | hostname = "tfacc-test-device-port" 25 | plan = "c3.medium.x86" 26 | facilities = ["sv15"] 27 | operating_system = "ubuntu_20_04" 28 | billing_cycle = "hourly" 29 | project_id = local.project_id 30 | } 31 | 32 | data "metal_port" "test" { 33 | device_id = metal_device.test.id 34 | name = "eth0" 35 | } 36 | ``` 37 | 38 | ## Argument Reference 39 | 40 | * `id` - (Required) ID of the port to read, conflicts with device_id. 41 | * `device_id` - (Required) 42 | * `name` - (Required) Whether to look for public or private block. 43 | 44 | ## Attributes Reference 45 | 46 | * `network_type` - One of layer2-bonded, layer2-individual, layer3, hybrid, hybrid-bonded 47 | * `type` - Type is either "NetworkBondPort" for bond ports or "NetworkPort" for bondable ethernet ports 48 | * `mac` - MAC address of the port 49 | * `bond_id` - UUID of the bond port" 50 | * `bond_name` - Name of the bond port 51 | * `bonded` - Flag indicating whether the port is bonded 52 | * `disbond_supported` - Flag indicating whether the port can be removed from a bond 53 | * `native_vlan_id` - UUID of native VLAN of the port 54 | * `vlan_ids` - UUIDs of attached VLANs 55 | * `vxlan_ids` - VXLAN ids of attached VLANs 56 | 57 | -------------------------------------------------------------------------------- /.github/workflows/acctest.yml: -------------------------------------------------------------------------------- 1 | name: Acceptance Tests 2 | on: 3 | push: 4 | paths-ignore: 5 | - 'LICENSE' 6 | - '**.md' 7 | - 'website/**' 8 | - 'docs/**' 9 | jobs: 10 | 11 | build: 12 | name: Build 13 | runs-on: ubuntu-latest 14 | timeout-minutes: 10 15 | steps: 16 | 17 | - name: Set up Go 18 | uses: actions/setup-go@v2.2.0 19 | with: 20 | go-version: '1.17.7' 21 | id: go 22 | 23 | - name: Check out code into the Go module directory 24 | uses: actions/checkout@v2.3.4 25 | 26 | - name: Get dependencies 27 | run: | 28 | go mod download 29 | - name: Build 30 | run: | 31 | go build -v . 32 | test: 33 | name: Matrix Test 34 | needs: build 35 | runs-on: ubuntu-latest 36 | timeout-minutes: 240 37 | strategy: 38 | fail-fast: false 39 | matrix: 40 | version: 41 | - stable 42 | terraform: 43 | - '1.1.6' 44 | steps: 45 | 46 | - name: Set up Go 47 | uses: actions/setup-go@v2.2.0 48 | with: 49 | go-version: '1.17.7' 50 | id: go 51 | 52 | - name: Check out code into the Go module directory 53 | uses: actions/checkout@v2.3.4 54 | 55 | - name: Get dependencies 56 | run: | 57 | go mod download 58 | 59 | - name: TF acceptance tests 60 | timeout-minutes: 180 61 | env: 62 | TF_ACC: "1" 63 | TF_ACC_TERRAFORM_VERSION: ${{ matrix.terraform }} 64 | # TF_SCHEMA_PANIC_ON_ERROR: "1" 65 | # TF_LOG: "DEBUG" 66 | # 67 | 68 | METAL_AUTH_TOKEN: ${{ secrets.PACKET_AUTH_TOKEN }} 69 | TF_ACC_METAL_DEDICATED_CONNECTION_ID: ${{ secrets.TF_ACC_METAL_DEDICATED_CONNECTION_ID }} 70 | run: | 71 | go test -v -coverprofile coverage.txt -covermode=atomic -parallel 8 -timeout 180m ./metal 72 | - name: Sweeper 73 | if: ${{ always() }} 74 | env: 75 | METAL_AUTH_TOKEN: ${{ secrets.PACKET_AUTH_TOKEN }} 76 | run: | 77 | go test ./metal -v -sweep="tf_test" 78 | - name: Upload coverage to Codecov 79 | if: ${{ always() }} 80 | uses: codecov/codecov-action@v2 81 | with: 82 | token: ${{ secrets.CODECOV_TOKEN }} 83 | files: ./coverage.txt 84 | -------------------------------------------------------------------------------- /docs/data-sources/reserved_ip_block.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_title: "Equinix Metal: reserved_ip_block" 3 | subcategory: "" 4 | description: |- 5 | Look up an IP address block 6 | --- 7 | 8 | # metal_reserved_ip_block (Data Source) 9 | 10 | !> **PROVIDER DEPRECATED:** Equinix Metal Provider is now Deprecated. Please consider using [`equinix_metal_reserved_ip_block`](https://registry.terraform.io/providers/equinix/equinix/latest/docs/data-sources/equinix_metal_reserved_ip_block) data source from the [Equinix provider](https://registry.terraform.io/providers/equinix/equinix/latest/docs) instead of `metal_reserved_ip_block`. [See the Metal provider section for more details](../index.md#equinix-metal-provider) on the new provider and available migration guides. 11 | 12 | Use this data source to find IP address blocks in Equinix Metal. You can use IP address or a block ID for lookup. 13 | 14 | ~> VRF features are not generally available. The interfaces related to VRF resources may change ahead of general availability. 15 | 16 | ## Example Usage 17 | 18 | Look up an IP address for a domain name, then use the IP to look up the containing IP block and run a device with IP address from the block: 19 | 20 | ```hcl 21 | data "dns_a_record_set" "www" { 22 | host = "www.example.com" 23 | } 24 | 25 | data "metal_reserved_ip_block" "www" { 26 | project_id = local.my_project_id 27 | address = data.dns_a_record_set.www.addrs[0] 28 | } 29 | 30 | resource "metal_device" "www" { 31 | project_id = local.my_project_id 32 | [...] 33 | ip_address { 34 | type = "public_ipv4" 35 | reservation_ids = [data.metal_reserved_ip_block.www.id] 36 | } 37 | } 38 | ``` 39 | 40 | ## Argument Reference 41 | 42 | * `id` - (Optional) UUID of the IP address block to look up 43 | * `project_id` - (Optional) UUID of the project where the searched block should be 44 | * `ip_address` - (Optional) Block containing this IP address will be returned 45 | 46 | -> **NOTE:** You should pass either `id`, or both `project_id` and `ip_address`. 47 | 48 | ## Attributes Reference 49 | 50 | This datasource exposes the same attributes as the [metal_reserved_ip_block resource](../resources/reserved_ip_block.md), with the following differences: 51 | 52 | * `type` - One of `global_ipv4`, `public_ipv4`, `private_ipv4`, `public_ipv6`,or `vrf` 53 | -------------------------------------------------------------------------------- /docs/data-sources/hardware_reservation.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_title: "Equinix Metal: metal_hardware_reservation" 3 | subcategory: "" 4 | description: |- 5 | Retrieve Equinix Metal Hardware Reservation 6 | --- 7 | 8 | # metal_hardware_reservation (Data Source) 9 | 10 | !> **PROVIDER DEPRECATED:** Equinix Metal Provider is now Deprecated. Please consider using [`equinix_metal_hardware_reservation`](https://registry.terraform.io/providers/equinix/equinix/latest/docs/data-sources/equinix_metal_hardware_reservation) data source from the [Equinix provider](https://registry.terraform.io/providers/equinix/equinix/latest/docs) instead of `metal_hardware_reservation`. [See the Metal provider section for more details](../index.md#equinix-metal-provider) on the new provider and available migration guides. 11 | 12 | Use this data source to retrieve a [hardware reservation resource from Equinix Metal](https://metal.equinix.com/developers/docs/deploy/reserved/). 13 | 14 | You can look up hardware reservation by its ID or by ID of device which occupies it. 15 | 16 | ## Example Usage 17 | 18 | ```hcl 19 | // lookup by ID 20 | data "hardware_reservation" "example" { 21 | id = "4347e805-eb46-4699-9eb9-5c116e6a0172" 22 | } 23 | 24 | // lookup by device ID 25 | data "hardware_reservation" "example_by_device_id" { 26 | device_id = "ff85aa58-c106-4624-8f1c-7c64554047ea" 27 | } 28 | ``` 29 | 30 | ## Argument Reference 31 | 32 | * `id` - ID of the hardware reservation 33 | * `device_id` - UUID of device occupying the reservation 34 | 35 | ## Attributes Reference 36 | 37 | * `id` - ID of the hardware reservation to look up 38 | * `short_id` - Reservation short ID 39 | * `project_id` - UUID of project this reservation is scoped to 40 | * `device_id` - UUID of device occupying the reservation 41 | * `plan` - Plan type for the reservation 42 | * `facility` - Plan type for the reservation 43 | * `provisionable` - Flag indicating whether the reserved server is provisionable or not. Spare devices can't be provisioned unless they are activated first 44 | * `spare` - Flag indicating whether the Hardware Reservation is a spare. Spare Hardware Reservations are used when a Hardware Reservations requires service from Metal Equinix 45 | * `switch_uuid` - Switch short ID, can be used to determine if two devices are connected to the same switch 46 | -------------------------------------------------------------------------------- /docs/data-sources/vlan.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_title: "Equinix Metal: metal_vlan" 3 | subcategory: "" 4 | description: |- 5 | Provides an Equinix Metal Virtual Network datasource. This can be used to read the attributes of existing VLANs. 6 | --- 7 | 8 | # metal_vlan (Data Source) 9 | 10 | !> **PROVIDER DEPRECATED:** Equinix Metal Provider is now Deprecated. Please consider using [`equinix_metal_vlan`](https://registry.terraform.io/providers/equinix/equinix/latest/docs/data-sources/equinix_metal_vlan) data source from the [Equinix provider](https://registry.terraform.io/providers/equinix/equinix/latest/docs) instead of `metal_vlan`. [See the Metal provider section for more details](../index.md#equinix-metal-provider) on the new provider and available migration guides. 11 | 12 | Provides an Equinix Metal Virtual Network datasource. VLANs data sources can be 13 | searched by VLAN UUID, or project UUID and vxlan number. 14 | 15 | ## Example Usage 16 | 17 | Fetch a vlan by ID: 18 | 19 | ```hcl 20 | resource "metal_vlan" "foovlan" { 21 | project_id = local.project_id 22 | metro = "sv" 23 | vxlan = 5 24 | } 25 | 26 | data "metal_vlan" "dsvlan" { 27 | vlan_id = metal_vlan.foovlan.id 28 | } 29 | ``` 30 | 31 | Fetch a vlan by project ID, vxlan and metro 32 | 33 | ```hcl 34 | resource "metal_vlan" "foovlan" { 35 | project_id = local.project_id 36 | metro = "sv" 37 | vxlan = 5 38 | } 39 | 40 | data "metal_vlan" "dsvlan" { 41 | project_id = local.project_id 42 | vxlan = 5 43 | metro = "sv" 44 | } 45 | ``` 46 | 47 | ## Argument Reference 48 | 49 | The following arguments are supported: 50 | 51 | * `vlan_id` - Metal UUID of the VLAN resource to look up 52 | * `project_id` - UUID of parent project of the VLAN. Use together with the vxlan number and metro or facility 53 | * `vxlan` - vxlan number of the VLAN to look up. Use together with the project_id and metro or facility 54 | * `facility` - Facility where the VLAN is deployed 55 | * `metro` - Metro where the VLAN is deployed 56 | 57 | ## Attributes Reference 58 | 59 | The following attributes are exported, in addition to any unspecified arguments. 60 | 61 | * `description` - Description text of the VLAN resource 62 | * `assigned_devices_ids` - List of device ID to which this VLAN is assigned 63 | -------------------------------------------------------------------------------- /metal/datasource_metal_spot_market_price.go: -------------------------------------------------------------------------------- 1 | package metal 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" 7 | "github.com/packethost/packngo" 8 | ) 9 | 10 | func dataSourceSpotMarketPrice() *schema.Resource { 11 | return &schema.Resource{ 12 | DeprecationMessage: deprecatedProviderMsg, 13 | Read: dataSourceMetalSpotMarketPriceRead, 14 | Schema: map[string]*schema.Schema{ 15 | "facility": { 16 | Type: schema.TypeString, 17 | Description: "Name of the facility", 18 | ConflictsWith: []string{"metro"}, 19 | Optional: true, 20 | }, 21 | "metro": { 22 | Type: schema.TypeString, 23 | Description: "Name of the metro", 24 | ConflictsWith: []string{"facility"}, 25 | Optional: true, 26 | StateFunc: toLower, 27 | }, 28 | "plan": { 29 | Type: schema.TypeString, 30 | Description: "Name of the plan", 31 | Required: true, 32 | }, 33 | "price": { 34 | Type: schema.TypeFloat, 35 | Description: "Current spot market price for given plan in given facility", 36 | Computed: true, 37 | }, 38 | }, 39 | } 40 | } 41 | 42 | func dataSourceMetalSpotMarketPriceRead(d *schema.ResourceData, meta interface{}) error { 43 | client := meta.(*packngo.Client) 44 | sms := client.SpotMarket.(*packngo.SpotMarketServiceOp) 45 | facility := d.Get("facility").(string) 46 | metro := d.Get("metro").(string) 47 | plan := d.Get("plan").(string) 48 | 49 | if facility != "" && metro != "" { 50 | return fmt.Errorf("Parameters facility and metro cannot be used together") 51 | } 52 | 53 | filter := facility 54 | fn := sms.PricesByFacility 55 | filterType := "facility" 56 | 57 | if metro != "" { 58 | filter = metro 59 | fn = sms.PricesByMetro 60 | filterType = "metro" 61 | } 62 | 63 | prices, _, err := fn() 64 | if err != nil { 65 | return err 66 | } 67 | 68 | match, ok := prices[filter] 69 | if !ok { 70 | return fmt.Errorf("Cannot find %s %s", filterType, filter) 71 | } 72 | 73 | price, ok := match[plan] 74 | if !ok { 75 | return fmt.Errorf("Cannot find price for plan %s in %s %s", plan, filterType, filter) 76 | } 77 | 78 | d.Set("price", price) 79 | d.SetId(fmt.Sprintf("%s-%s-%s", filterType, filter, plan)) 80 | return nil 81 | } 82 | -------------------------------------------------------------------------------- /metal/datasource_metal_organization_test.go: -------------------------------------------------------------------------------- 1 | package metal 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" 8 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" 9 | "github.com/packethost/packngo" 10 | ) 11 | 12 | func TestAccOrgDataSource_Basic(t *testing.T) { 13 | var org packngo.Organization 14 | rInt := acctest.RandInt() 15 | 16 | resource.ParallelTest(t, resource.TestCase{ 17 | PreCheck: func() { testAccPreCheck(t) }, 18 | Providers: testAccProviders, 19 | CheckDestroy: testAccCheckMetalOrgDestroy, 20 | Steps: []resource.TestStep{ 21 | { 22 | Config: testAccCheckMetalOrgDataSourceConfigBasic(rInt), 23 | Check: resource.ComposeTestCheckFunc( 24 | testAccCheckMetalOrgExists("metal_organization.test", &org), 25 | resource.TestCheckResourceAttr( 26 | "metal_organization.test", "name", 27 | fmt.Sprintf("tfacc-org-datasource-%d", rInt)), 28 | resource.TestCheckResourceAttr( 29 | "metal_organization.test", "description", "quux"), 30 | resource.TestCheckResourceAttr( 31 | "data.metal_organization.test", "name", 32 | fmt.Sprintf("tfacc-org-datasource-%d", rInt)), 33 | resource.TestCheckResourceAttrPair( 34 | "metal_organization.test", "address.0.address", 35 | "data.metal_organization.test", "address.0.address", 36 | ), 37 | resource.TestCheckResourceAttrPair( 38 | "metal_organization.test", "address.0.city", 39 | "data.metal_organization.test", "address.0.city", 40 | ), 41 | resource.TestCheckResourceAttrPair( 42 | "metal_organization.test", "address.0.country", 43 | "data.metal_organization.test", "address.0.country", 44 | ), 45 | resource.TestCheckResourceAttrPair( 46 | "metal_organization.test", "address.0.zip_code", 47 | "data.metal_organization.test", "address.0.zip_code", 48 | ), 49 | ), 50 | }, 51 | }, 52 | }) 53 | } 54 | 55 | func testAccCheckMetalOrgDataSourceConfigBasic(r int) string { 56 | return fmt.Sprintf(` 57 | resource "metal_organization" "test" { 58 | name = "tfacc-org-datasource-%d" 59 | description = "quux" 60 | address { 61 | address = "tfacc org street" 62 | city = "london" 63 | zip_code = "12345" 64 | country = "GB" 65 | } 66 | } 67 | 68 | data "metal_organization" "test" { 69 | organization_id = metal_organization.test.id 70 | } 71 | 72 | `, r) 73 | } 74 | -------------------------------------------------------------------------------- /docs/data-sources/project.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_title: "Equinix Metal: metal_project" 3 | subcategory: "" 4 | description: |- 5 | Provides an Equinix Metal Project datasource. 6 | --- 7 | 8 | # metal_project (Data Source) 9 | 10 | !> **PROVIDER DEPRECATED:** Equinix Metal Provider is now Deprecated. Please consider using [`equinix_metal_project`](https://registry.terraform.io/providers/equinix/equinix/latest/docs/data-sources/equinix_metal_project) data source from the [Equinix provider](https://registry.terraform.io/providers/equinix/equinix/latest/docs) instead of `metal_project`. [See the Metal provider section for more details](../index.md#equinix-metal-provider) on the new provider and available migration guides. 11 | 12 | Use this datasource to retrieve attributes of the Project API resource. 13 | 14 | ## Example Usage 15 | 16 | ```hcl 17 | # Get Project by name and print UUIDs of its users 18 | data "metal_project" "tf_project_1" { 19 | name = "Terraform Fun" 20 | } 21 | 22 | output "users_of_Terraform_Fun" { 23 | value = data.metal_project.tf_project_1.user_ids 24 | } 25 | ``` 26 | 27 | ## Argument Reference 28 | 29 | The following arguments are supported: 30 | 31 | * `name` - The name which is used to look up the project 32 | * `project_id` - The UUID by which to look up the project 33 | 34 | ## Attributes Reference 35 | 36 | The following attributes are exported: 37 | 38 | * `payment_method_id` - The UUID of payment method for this project 39 | * `organization_id` - The UUID of this project's parent organization 40 | * `backend_transfer` - Whether Backend Transfer is enabled for this project 41 | * `created` - The timestamp for when the project was created 42 | * `updated` - The timestamp for the last time the project was updated 43 | * `user_ids` - List of UUIDs of user accounts which belong to this project 44 | * `bgp_config` - Optional BGP settings. Refer to [Equinix Metal guide for BGP](https://metal.equinix.com/developers/docs/networking/local-global-bgp/). 45 | 46 | The `bgp_config` block contains: 47 | 48 | * `asn` - Autonomous System Number for local BGP deployment 49 | * `md5` - Password for BGP session in plaintext (not a checksum) 50 | * `deployment_type` - `private` or `public`, the `private` is likely to be usable immediately, the `public` will need to be review by Equinix Metal engineers 51 | * `status` - status of BGP configuration in the project 52 | * `max_prefix` - The maximum number of route filters allowed per server 53 | -------------------------------------------------------------------------------- /docs/resources/device_network_type.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: "metal" 3 | page_title: "Equinix Metal: metal_device_network_type" 4 | sidebar_current: "docs-metal-resource-device-network-type" 5 | description: |- 6 | Provides a resource to manage network type of Equinix Metal devices. 7 | --- 8 | 9 | # metal_device_network_type (Resource) 10 | 11 | !> **PROVIDER DEPRECATED:** Equinix Metal Provider is now Deprecated. Please consider using [`equinix_metal_device_network_type`](https://registry.terraform.io/providers/equinix/equinix/latest/docs/resources/equinix_metal_device_network_type) resource from the [Equinix provider](https://registry.terraform.io/providers/equinix/equinix/latest/docs) instead of `metal_device_network_type`. [See the Metal provider section for more details](../index.md#equinix-metal-provider) on the new provider and available migration guides. 12 | 13 | This resource controls network type of Equinix Metal devices. 14 | 15 | To learn more about Layer 2 networking in Equinix Metal, refer to 16 | 17 | * 18 | * 19 | 20 | If you are attaching VLAN to a device (i.e. using metal_port_vlan_attachment), link the device ID from this resource, in order to make the port attachment implicitly dependent on the state of the network type. If you link the device ID from the metal_device resource, Terraform will not wait for the network type change. See examples in [metal_port_vlan_attachment](port_vlan_attachment). 21 | 22 | ## Example Usage 23 | 24 | See the [Network Types Guide](../guides/network_types.md) for examples of this resource and to learn about the recommended `metal_port` alternative. 25 | 26 | ## Import 27 | 28 | This resource can also be imported using existing device ID: 29 | 30 | ```sh 31 | terraform import metal_device_network_type {existing device_id} 32 | ``` 33 | 34 | ## Argument Reference 35 | 36 | The following arguments are supported: 37 | 38 | * `device_id` - (Required) The ID of the device on which the network type should be set. 39 | * `type` - (Required) Network type to set. Must be one of `layer3`, `hybrid`, `layer2-individual` and `layer2-bonded`. 40 | 41 | ## Attributes Reference 42 | 43 | The following attributes are exported: 44 | 45 | * `id` - ID of the controlled device. Use this in linked resources, if you need to wait for the network type change. It is the same as `device_id`. 46 | -------------------------------------------------------------------------------- /metal/datasource_metal_metro_test.go: -------------------------------------------------------------------------------- 1 | package metal 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" 8 | ) 9 | 10 | func TestAccDataSourceMetro_Basic(t *testing.T) { 11 | testMetro := "da" 12 | resource.ParallelTest(t, resource.TestCase{ 13 | PreCheck: func() { testAccPreCheck(t) }, 14 | Providers: testAccProviders, 15 | Steps: []resource.TestStep{ 16 | { 17 | Config: testAccDataSourceMetroConfigBasic(testMetro), 18 | Check: resource.ComposeTestCheckFunc( 19 | resource.TestCheckResourceAttr( 20 | "data.metal_metro.test", "code", testMetro), 21 | ), 22 | }, 23 | { 24 | Config: testAccDataSourceMetroConfigCapacityReasonable(testMetro), 25 | Check: resource.ComposeTestCheckFunc( 26 | resource.TestCheckResourceAttr( 27 | "data.metal_metro.test", "code", testMetro), 28 | ), 29 | }, 30 | { 31 | Config: testAccDataSourceMetroConfigCapacityUnreasonable(testMetro), 32 | ExpectError: matchErrNoCapacity, 33 | }, 34 | { 35 | Config: testAccDataSourceMetroConfigCapacityUnreasonableMultiple(testMetro), 36 | ExpectError: matchErrNoCapacity, 37 | }, 38 | }, 39 | }) 40 | } 41 | 42 | func testAccDataSourceMetroConfigBasic(facCode string) string { 43 | return fmt.Sprintf(` 44 | data "metal_metro" "test" { 45 | code = "%s" 46 | } 47 | `, facCode) 48 | } 49 | 50 | func testAccDataSourceMetroConfigCapacityUnreasonable(facCode string) string { 51 | return fmt.Sprintf(` 52 | data "metal_metro" "test" { 53 | code = "%s" 54 | capacity { 55 | plan = "c3.small.x86" 56 | quantity = 1000 57 | } 58 | } 59 | `, facCode) 60 | } 61 | 62 | func testAccDataSourceMetroConfigCapacityReasonable(facCode string) string { 63 | return fmt.Sprintf(` 64 | data "metal_metro" "test" { 65 | code = "%s" 66 | capacity { 67 | plan = "c3.small.x86" 68 | quantity = 1 69 | } 70 | capacity { 71 | plan = "c3.medium.x86" 72 | quantity = 1 73 | } 74 | } 75 | `, facCode) 76 | } 77 | 78 | func testAccDataSourceMetroConfigCapacityUnreasonableMultiple(facCode string) string { 79 | return fmt.Sprintf(` 80 | data "metal_metro" "test" { 81 | code = "%s" 82 | capacity { 83 | plan = "c3.small.x86" 84 | quantity = 1 85 | } 86 | capacity { 87 | plan = "c3.medium.x86" 88 | quantity = 1000 89 | } 90 | } 91 | `, facCode) 92 | } 93 | -------------------------------------------------------------------------------- /metal/datasource_metal_volume.go: -------------------------------------------------------------------------------- 1 | package metal 2 | 3 | import ( 4 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" 5 | ) 6 | 7 | func dataSourceMetalVolume() *schema.Resource { 8 | return &schema.Resource{ 9 | Read: removedResourceOp(dataSourceVolumeRemovedMsg), 10 | DeprecationMessage: "Volumes are deprecated, see https://metal.equinix.com/developers/docs/resilience-recovery/elastic-block-storage/#elastic-block-storage", 11 | Schema: map[string]*schema.Schema{ 12 | "name": { 13 | Type: schema.TypeString, 14 | Optional: true, 15 | Computed: true, 16 | ConflictsWith: []string{"volume_id"}, 17 | }, 18 | "project_id": { 19 | Type: schema.TypeString, 20 | Optional: true, 21 | Computed: true, 22 | ConflictsWith: []string{"volume_id"}, 23 | }, 24 | "volume_id": { 25 | Type: schema.TypeString, 26 | Optional: true, 27 | Computed: true, 28 | ConflictsWith: []string{"project_id", "name"}, 29 | }, 30 | "description": { 31 | Type: schema.TypeString, 32 | Computed: true, 33 | }, 34 | "size": { 35 | Type: schema.TypeInt, 36 | Computed: true, 37 | }, 38 | 39 | "facility": { 40 | Type: schema.TypeString, 41 | Computed: true, 42 | }, 43 | 44 | "plan": { 45 | Type: schema.TypeString, 46 | Computed: true, 47 | }, 48 | 49 | "billing_cycle": { 50 | Type: schema.TypeString, 51 | Computed: true, 52 | }, 53 | 54 | "state": { 55 | Type: schema.TypeString, 56 | Computed: true, 57 | }, 58 | 59 | "locked": { 60 | Type: schema.TypeBool, 61 | Computed: true, 62 | }, 63 | 64 | "snapshot_policies": { 65 | Type: schema.TypeList, 66 | Computed: true, 67 | Elem: &schema.Resource{ 68 | Schema: map[string]*schema.Schema{ 69 | "snapshot_frequency": { 70 | Type: schema.TypeString, 71 | Computed: true, 72 | }, 73 | "snapshot_count": { 74 | Type: schema.TypeInt, 75 | Computed: true, 76 | }, 77 | }, 78 | }, 79 | }, 80 | 81 | "device_ids": { 82 | Type: schema.TypeList, 83 | Computed: true, 84 | Elem: &schema.Schema{Type: schema.TypeString}, 85 | }, 86 | 87 | "created": { 88 | Type: schema.TypeString, 89 | Computed: true, 90 | }, 91 | 92 | "updated": { 93 | Type: schema.TypeString, 94 | Computed: true, 95 | }, 96 | }, 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /metal/internal/datalist/sort.go: -------------------------------------------------------------------------------- 1 | package datalist 2 | 3 | import ( 4 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" 5 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" 6 | "sort" 7 | "strings" 8 | ) 9 | 10 | var ( 11 | sortAttributes = []string{"asc", "desc"} 12 | ) 13 | 14 | type commonSort struct { 15 | attribute string 16 | direction string 17 | } 18 | 19 | func sortSchema(allowedAttributes []string) *schema.Schema { 20 | return &schema.Schema{ 21 | Type: schema.TypeList, 22 | Elem: &schema.Resource{ 23 | Schema: map[string]*schema.Schema{ 24 | "attribute": { 25 | Type: schema.TypeString, 26 | Description: "The attribute used to sort the results. Sort attributes are case-sensitive", 27 | Required: true, 28 | ValidateFunc: validation.StringInSlice(allowedAttributes, false), 29 | }, 30 | "direction": { 31 | Type: schema.TypeString, 32 | Description: "Sort results in ascending or descending order. Strings are sorted in alphabetical order. One of: asc, desc", 33 | Optional: true, 34 | ValidateFunc: validation.StringInSlice(sortAttributes, false), 35 | }, 36 | }, 37 | }, 38 | Optional: true, 39 | Description: "One or more attribute/direction pairs on which to sort results. If multiple sorts are provided, they will be applied in order", 40 | } 41 | } 42 | 43 | func expandSorts(rawSorts []interface{}) []commonSort { 44 | expandedSorts := make([]commonSort, len(rawSorts)) 45 | for i, rawSort := range rawSorts { 46 | f := rawSort.(map[string]interface{}) 47 | 48 | expandedSort := commonSort{ 49 | attribute: f["attribute"].(string), 50 | direction: f["direction"].(string), 51 | } 52 | 53 | expandedSorts[i] = expandedSort 54 | } 55 | return expandedSorts 56 | } 57 | 58 | func applySorts(recordSchema map[string]*schema.Schema, records []map[string]interface{}, sorts []commonSort) []map[string]interface{} { 59 | sort.Slice(records, func(_i, _j int) bool { 60 | for _, s := range sorts { 61 | // Handle multiple sorts by applying them in order 62 | i := _i 63 | j := _j 64 | if strings.EqualFold(s.direction, "desc") { 65 | // If the direction is desc, reverse index to compare 66 | i = _j 67 | j = _i 68 | } 69 | 70 | value1 := records[i] 71 | value2 := records[j] 72 | cmp := compareValues(recordSchema[s.attribute], value1[s.attribute], value2[s.attribute]) 73 | if cmp != 0 { 74 | return cmp < 0 75 | } 76 | } 77 | 78 | return true 79 | }) 80 | 81 | return records 82 | } 83 | -------------------------------------------------------------------------------- /docs/data-sources/device_bgp_neighbors.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_title: "Equinix Metal: metal_device_bgp_neighbors" 3 | subcategory: "" 4 | description: |- 5 | Provides a datasource for listing BGP neighbors of an Equinix Metal device 6 | --- 7 | 8 | # metal_device_bgp_neighbors (Data Source) 9 | 10 | !> **PROVIDER DEPRECATED:** Equinix Metal Provider is now Deprecated. Please consider using [`equinix_metal_device_bgp_neighbors`](https://registry.terraform.io/providers/equinix/equinix/latest/docs/data-sources/equinix_metal_device_bgp_neighbors) data source from the [Equinix provider](https://registry.terraform.io/providers/equinix/equinix/latest/docs) instead of `metal_device_bgp_neighbors`. [See the Metal provider section for more details](../index.md#equinix-metal-provider) on the new provider and available migration guides. 11 | 12 | Use this datasource to retrieve list of BGP neighbors of a device in the Equinix Metal host. 13 | 14 | To have any BGP neighbors listed, the device must be in [BGP-enabled project](../r/project.html) and have a [BGP session](../r/bgp_session.html) assigned. 15 | 16 | To learn more about using BGP in Equinix Metal, see the [metal_bgp_session](../r/bgp_session.html) resource documentation. 17 | 18 | ## Example Usage 19 | 20 | ```hcl 21 | # Get Project by name and print UUIDs of its users 22 | 23 | data "metal_device_bgp_neighbors" "test" { 24 | device_id = "4c641195-25e5-4c3c-b2b7-4cd7a42c7b40" 25 | } 26 | 27 | output "bgp_neighbors_listing" { 28 | value = data.metal_device_bgp_neighbors.test.bgp_neighbors 29 | } 30 | ``` 31 | 32 | ## Argument Reference 33 | 34 | The following arguments are supported: 35 | 36 | * `device_id` - UUID of BGP-enabled device whose neighbors to list 37 | 38 | ## Attributes Reference 39 | 40 | The following attributes are exported: 41 | 42 | * `bgp_neighbors` - array of BGP neighbor records with attributes: 43 | * `address_family` - IP address version, 4 or 6 44 | * `customer_as` - Local autonomous system number 45 | * `customer_ip` - Local used peer IP address 46 | * `md5_enabled` - Whether BGP session is password enabled 47 | * `md5_password` - BGP session password in plaintext (not a checksum) 48 | * `multihop` - Whether the neighbor is in EBGP multihop session 49 | * `peer_as` - Peer AS number (different than customer_as for EBGP) 50 | * `peer_ips` - Array of IP addresses of this neighbor's peers 51 | * `routes_in` - Array of incoming routes. Each route has attributes: 52 | * `route` - CIDR expression of route (IP/mask) 53 | * `exact` - (bool) Whether the route is exact 54 | * `routes_out` - Array of outgoing routes in the same format 55 | -------------------------------------------------------------------------------- /docs/resources/ssh_key.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_title: "Equinix Metal: metal_ssh_key" 3 | subcategory: "" 4 | description: |- 5 | Provides an Equinix Metal SSH key resource. 6 | --- 7 | 8 | # metal_ssh_key (Resource) 9 | 10 | !> **PROVIDER DEPRECATED:** Equinix Metal Provider is now Deprecated. Please consider using [`equinix_metal_ssh_key`](https://registry.terraform.io/providers/equinix/equinix/latest/docs/resources/equinix_metal_ssh_key) resource from the [Equinix provider](https://registry.terraform.io/providers/equinix/equinix/latest/docs) instead of `metal_ssh_key`. [See the Metal provider section for more details](../index.md#equinix-metal-provider) on the new provider and available migration guides. 11 | 12 | Provides a resource to manage User SSH keys on your Equinix Metal user account. If you create a new device in a project, all the keys of the project's collaborators will be injected to the device. 13 | 14 | The link between User SSH key and device is implicit. If you want to make sure that a key will be copied to a device, you must ensure that the device resource `depends_on` the key resource. 15 | 16 | ## Example Usage 17 | 18 | ```hcl 19 | # Create a new SSH key 20 | resource "metal_ssh_key" "key1" { 21 | name = "terraform-1" 22 | public_key = file("/home/terraform/.ssh/id_rsa.pub") 23 | } 24 | 25 | # Create new device with "key1" included. The device resource "depends_on" the 26 | # key, in order to make sure the key is created before the device. 27 | resource "metal_device" "test" { 28 | hostname = "test-device" 29 | plan = "c3.small.x86" 30 | metro = "sv" 31 | operating_system = "ubuntu_20_04" 32 | billing_cycle = "hourly" 33 | project_id = local.project_id 34 | depends_on = ["metal_ssh_key.key1"] 35 | } 36 | ``` 37 | 38 | ## Argument Reference 39 | 40 | The following arguments are supported: 41 | 42 | * `name` - (Required) The name of the SSH key for identification 43 | * `public_key` - (Required) The public key. If this is a file, it 44 | can be read using the file interpolation function 45 | 46 | ## Attributes Reference 47 | 48 | In addition to all arguments above, the following attributes are exported: 49 | 50 | * `id` - The unique ID of the key 51 | * `fingerprint` - The fingerprint of the SSH key 52 | * `owner_id` - The UUID of the Equinix Metal API User who owns this key 53 | * `created` - The timestamp for when the SSH key was created 54 | * `updated` - The timestamp for the last time the SSH key was updated 55 | 56 | ## Import 57 | 58 | This resource can be imported using an existing SSH Key ID: 59 | 60 | ```sh 61 | terraform import metal_ssh_key {existing_sshkey_id} 62 | ``` 63 | -------------------------------------------------------------------------------- /docs/resources/gateway.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_title: "Equinix Metal: Metal Gateway" 3 | subcategory: "" 4 | description: |- 5 | Create Equinix Metal Gateways 6 | --- 7 | 8 | # metal_gateway (Resource) 9 | 10 | !> **PROVIDER DEPRECATED:** Equinix Metal Provider is now Deprecated. Please consider using [`equinix_metal_gateway`](https://registry.terraform.io/providers/equinix/equinix/latest/docs/resources/equinix_metal_gateway) resource from the [Equinix provider](https://registry.terraform.io/providers/equinix/equinix/latest/docs) instead of `metal_gateway`. [See the Metal provider section for more details](../index.md#equinix-metal-provider) on the new provider and available migration guides. 11 | 12 | Use this resource to create Metal Gateway resources in Equinix Metal. 13 | 14 | ~> VRF features are not generally available. The interfaces related to VRF resources may change ahead of general availability. 15 | 16 | ## Example Usage 17 | 18 | ```hcl 19 | # Create Metal Gateway for a VLAN with a private IPv4 block with 8 IP addresses 20 | 21 | resource "metal_vlan" "test" { 22 | description = "test VLAN in SV" 23 | metro = "sv" 24 | project_id = local.project_id 25 | } 26 | 27 | resource "metal_gateway" "test" { 28 | project_id = local.project_id 29 | vlan_id = metal_vlan.test.id 30 | private_ipv4_subnet_size = 8 31 | } 32 | ``` 33 | 34 | ```hcl 35 | # Create Metal Gateway for a VLAN and reserved IP address block 36 | 37 | resource "metal_vlan" "test" { 38 | description = "test VLAN in SV" 39 | metro = "sv" 40 | project_id = local.project_id 41 | } 42 | 43 | resource "metal_reserved_ip_block" "test" { 44 | project_id = local.project_id 45 | metro = "sv" 46 | quantity = 2 47 | } 48 | 49 | resource "metal_gateway" "test" { 50 | project_id = local.project_id 51 | vlan_id = metal_vlan.test.id 52 | ip_reservation_id = metal_reserved_ip_block.test.id 53 | } 54 | ``` 55 | 56 | ## Argument Reference 57 | 58 | * `project_id` - (Required) UUID of the project where the gateway is scoped to. 59 | * `vlan_id` - (Required) UUID of the VLAN where the gateway is scoped to. 60 | * `ip_reservation_id` - (Optional) UUID of Public or VRF IP Reservation to associate with the gateway, the reservation must be in the same metro as the VLAN, conflicts with `private_ipv4_subnet_size`. 61 | * `private_ipv4_subnet_size` - (Optional) Size of the private IPv4 subnet to create for this metal gateway, must be one of (8, 16, 32, 64, 128), conflicts with `ip_reservation_id`. 62 | 63 | ## Attributes Reference 64 | 65 | * `state` - Status of the gateway resource. 66 | * `vrf_id` - UUID of the VRF associated with the IP Reservation. 67 | -------------------------------------------------------------------------------- /docs/data-sources/ip_block_ranges.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_title: "Equinix Metal: ip_block_ranges" 3 | subcategory: "" 4 | description: |- 5 | List IP address ranges allocated to a project 6 | --- 7 | 8 | # metal_ip_block_ranges (Data Source) 9 | 10 | !> **PROVIDER DEPRECATED:** Equinix Metal Provider is now Deprecated. Please consider using [`equinix_metal_ip_block_ranges`](https://registry.terraform.io/providers/equinix/equinix/latest/docs/data-sources/equinix_metal_ip_block_ranges) data source from the [Equinix provider](https://registry.terraform.io/providers/equinix/equinix/latest/docs) instead of `metal_ip_block_ranges`. [See the Metal provider section for more details](../index.md#equinix-metal-provider) on the new provider and available migration guides. 11 | 12 | Use this datasource to get CIDR expressions for allocated IP blocks of all the types in a project, optionally filtered by facility or metro. 13 | 14 | There are four types of IP blocks in Equinix Metal: global IPv4, public IPv4, private IPv4 and IPv6. Both global and public IPv4 are routable from the Internet. Public IPv4 blocks are allocated in a facility or metro, and addresses from it can only be assigned to devices in that location. Addresses from Global IPv4 block can be assigned to a device in any metro. 15 | 16 | The datasource has 4 list attributes: `global_ipv4`, `public_ipv4`, `private_ipv4` and `ipv6`, each listing CIDR notation (`/`) of respective blocks from the project. 17 | 18 | ## Example Usage 19 | 20 | ```hcl 21 | # List CIDR expressions of all the allocated IP block in you project. 22 | 23 | # Declare your project ID 24 | locals { 25 | project_id = "" 26 | } 27 | 28 | data "metal_ip_block_ranges" "test" { 29 | project_id = local.project_id 30 | } 31 | 32 | output "out" { 33 | value = data.metal_ip_block_ranges.test 34 | } 35 | ``` 36 | 37 | ## Argument Reference 38 | 39 | * `project_id` - (Required) ID of the project from which to list the blocks. 40 | * `facility` - (Optional) Facility code filtering the IP blocks. Global IPv4 blcoks will be listed anyway. If you omit this and metro, all the block from the project will be listed. 41 | * `metro` - (Optional) Metro code filtering the IP blocks. Global IPv4 blcoks will be listed anyway. If you omit this and facility, all the block from the project will be listed. 42 | 43 | ## Attributes Reference 44 | 45 | * `global_ipv4` - list of CIDR expressions for Global IPv4 blocks in the project 46 | * `public_ipv4` - list of CIDR expressions for Public IPv4 blocks in the project 47 | * `private_ipv4` - list of CIDR expressions for Private IPv4 blocks in the project 48 | * `ipv6` - list of CIDR expressions for IPv6 blocks in the project 49 | -------------------------------------------------------------------------------- /docs/data-sources/precreated_ip_block.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_title: "Equinix Metal: precreated_ip_block" 3 | subcategory: "" 4 | description: |- 5 | Load automatically created IP blocks from your Equinix Metal project 6 | --- 7 | 8 | # metal_precreated_ip_block (Data Source) 9 | 10 | !> **PROVIDER DEPRECATED:** Equinix Metal Provider is now Deprecated. Please consider using [`equinix_metal_precreated_ip_block`](https://registry.terraform.io/providers/equinix/equinix/latest/docs/data-sources/equinix_metal_precreated_ip_block) data source from the [Equinix provider](https://registry.terraform.io/providers/equinix/equinix/latest/docs) instead of `metal_precreated_ip_block`. [See the Metal provider section for more details](../index.md#equinix-metal-provider) on the new provider and available migration guides. 11 | 12 | Use this data source to get CIDR expression for precreated IPv6 and IPv4 blocks in Equinix Metal. 13 | You can then use the cidrsubnet TF builtin function to derive subnets. 14 | 15 | ## Example Usage 16 | 17 | ```hcl 18 | # Create device in your project and then assign /64 subnet from precreated block 19 | # to the new device 20 | 21 | # Declare your project ID 22 | locals { 23 | project_id = "" 24 | } 25 | 26 | resource "metal_device" "web1" { 27 | hostname = "web1" 28 | plan = "c3.small.x86" 29 | metro = "sv" 30 | operating_system = "ubuntu_20_04" 31 | billing_cycle = "hourly" 32 | project_id = local.project_id 33 | 34 | } 35 | 36 | data "metal_precreated_ip_block" "test" { 37 | metro = "sv" 38 | project_id = local.project_id 39 | address_family = 6 40 | public = true 41 | } 42 | 43 | # The precreated IPv6 blocks are /56, so to get /64, we specify 8 more bits for network. 44 | # The cirdsubnet interpolation will pick second /64 subnet from the precreated block. 45 | 46 | resource "metal_ip_attachment" "from_ipv6_block" { 47 | device_id = metal_device.web1.id 48 | cidr_notation = cidrsubnet(data.metal_precreated_ip_block.test.cidr_notation, 8, 2) 49 | } 50 | ``` 51 | 52 | ## Argument Reference 53 | 54 | * `project_id` - (Required) ID of the project where the searched block should be. 55 | * `address_family` - (Required) 4 or 6, depending on which block you are looking for. 56 | * `public` - (Required) Whether to look for public or private block. 57 | * `global` - (Optional) Whether to look for global block. Default is false for backward compatibility. 58 | * `facility` - (Optional) Facility of the searched block. (for non-global blocks). 59 | * `metro` - (Optional) Metro of the searched block (for non-global blocks). 60 | 61 | ## Attributes Reference 62 | 63 | * `cidr_notation` - CIDR notation of the looked up block. 64 | -------------------------------------------------------------------------------- /docs/resources/port.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_title: "Equinix Metal: metal_port" 3 | subcategory: "" 4 | description: |- 5 | Manipulate device ports 6 | --- 7 | 8 | # metal_port (Resource) 9 | 10 | !> **PROVIDER DEPRECATED:** Equinix Metal Provider is now Deprecated. Please consider using [`equinix_metal_port`](https://registry.terraform.io/providers/equinix/equinix/latest/docs/resources/equinix_metal_port) resource from the [Equinix provider](https://registry.terraform.io/providers/equinix/equinix/latest/docs) instead of `metal_port`. [See the Metal provider section for more details](../index.md#equinix-metal-provider) on the new provider and available migration guides. 11 | 12 | Use this resource to set up network ports on an Equnix Metal device. This resource can control both physical and bond ports. 13 | 14 | This Terraform resource doesn't create an API resource in Equinix Metal, but rather provides finer control for [`Layer 2 networking`](https://metal.equinix.com/developers/docs/layer2-networking/). 15 | 16 | The port resource referred is created together with device and accessible either via the device resource or over `/port/` API path. 17 | 18 | ## Example Usage 19 | 20 | See the [Network Types Guide](../guides/network_types.md) for examples of this resource. 21 | 22 | ## Argument Reference 23 | 24 | * `port_id` - (Required) ID of the port to read 25 | * `bonded` - (Required) Whether the port should be bonded 26 | * `layer2` - (Optional) Whether to put the port to Layer 2 mode, valid only for bond ports 27 | * `vlan_ids` - (Optional) List of VLAN UUIDs to attach to the port, valid only for L2 and Hybrid ports 28 | * `vxlan_ids` - (Optional) List of VXLAN IDs to attach to the port, valid only for L2 and Hybrid ports 29 | * `native_vlan_id` - (Optional) UUID of a VLAN to assign as a native VLAN. It must be one of attached VLANs (from `vlan_ids` parameter), valid only for physical (non-bond) ports 30 | * `reset_on_delete` - (Optional) Behavioral setting to reset the port to default settings. For a bond port it means layer3 without vlans attached, eth ports will be bonded without native vlan and vlans attached 31 | 32 | ## Attributes Reference 33 | 34 | * `name` - Name of the port, e.g. `bond0` or `eth0` 35 | * `network_type` - One of layer2-bonded, layer2-individual, layer3, hybrid and hybrid-bonded. This attribute is only set on bond ports. 36 | * `type` - Type is either "NetworkBondPort" for bond ports or "NetworkPort" for bondable ethernet ports 37 | * `mac` - MAC address of the port 38 | * `bond_id` - UUID of the bond port 39 | * `bond_name` - Name of the bond port 40 | * `bonded` - Flag indicating whether the port is bonded 41 | * `disbond_supported` - Flag indicating whether the port can be removed from a bond 42 | * `vlan_ids` - List of VLAN UUIDs to attach to the port 43 | * `vxlan_ids` - List of VXLAN IDs to attach to the port 44 | 45 | -------------------------------------------------------------------------------- /metal/resource_metal_user_api_key_test.go: -------------------------------------------------------------------------------- 1 | package metal 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "net/http" 7 | "strings" 8 | 9 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" 10 | "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" 11 | "github.com/packethost/packngo" 12 | ) 13 | 14 | func init() { 15 | resource.AddTestSweepers("metal_user_api_key", &resource.Sweeper{ 16 | Name: "metal_user_api_key", 17 | F: testSweepUserAPIKeys, 18 | }) 19 | } 20 | 21 | // Will remove all User API keys with Description starting with "tfacc-" 22 | func testSweepUserAPIKeys(region string) error { 23 | log.Printf("[DEBUG] Sweeping user_api keys") 24 | meta, err := sharedConfigForRegion(region) 25 | if err != nil { 26 | return fmt.Errorf("Error getting client for sweeping user_api keys: %s", err) 27 | } 28 | client := meta.(*packngo.Client) 29 | 30 | userApiKeys, _, err := client.APIKeys.UserList(nil) 31 | if err != nil { 32 | return fmt.Errorf("Error getting list for sweeping user_api keys: %s", err) 33 | } 34 | ids := []string{} 35 | for _, k := range userApiKeys { 36 | if strings.HasPrefix(k.Description, "tfacc-") { 37 | ids = append(ids, k.ID) 38 | } 39 | } 40 | for _, id := range ids { 41 | log.Printf("Removing user api key %s", id) 42 | resp, err := client.APIKeys.Delete(id) 43 | if err != nil && resp.StatusCode != http.StatusNotFound { 44 | return fmt.Errorf("Error deleting user_api key %s", err) 45 | } 46 | } 47 | return nil 48 | } 49 | 50 | func testAccMetalUserAPIKeyDestroy(s *terraform.State) error { 51 | client := testAccProvider.Meta().(*packngo.Client) 52 | for _, rs := range s.RootModule().Resources { 53 | if rs.Type != "metal_user_api_key" { 54 | continue 55 | } 56 | if _, err := client.APIKeys.UserGet(rs.Primary.ID, nil); err == nil { 57 | return fmt.Errorf("UserAPI key still exists") 58 | } 59 | } 60 | return nil 61 | } 62 | 63 | func testAccMetalUserAPIKeyConfig_Basic() string { 64 | return fmt.Sprintf(` 65 | resource "metal_user_api_key" "test" { 66 | description = "tfacc-user-key" 67 | read_only = true 68 | }`) 69 | } 70 | 71 | // Commented out because it lists existing user API keys in debug log 72 | 73 | /* 74 | 75 | func TestAccMetalUserAPIKey_Basic(t *testing.T) { 76 | resource.ParallelTest(t, resource.TestCase{ 77 | PreCheck: func() { testAccPreCheck(t) }, 78 | Providers: testAccProviders, 79 | CheckDestroy: testAccMetalUserAPIKeyDestroy, 80 | Steps: []resource.TestStep{ 81 | { 82 | Config: testAccMetalUserAPIKeyConfig_Basic(), 83 | Check: resource.ComposeTestCheckFunc( 84 | resource.TestCheckResourceAttrSet( 85 | "metal_user_api_key.test", "token"), 86 | resource.TestCheckResourceAttrSet( 87 | "metal_user_api_key.test", "user_id"), 88 | ), 89 | }, 90 | }, 91 | }) 92 | } 93 | 94 | */ 95 | -------------------------------------------------------------------------------- /metal/datasource_metal_port.go: -------------------------------------------------------------------------------- 1 | package metal 2 | 3 | import ( 4 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" 5 | ) 6 | 7 | func dataSourceMetalPort() *schema.Resource { 8 | return &schema.Resource{ 9 | DeprecationMessage: deprecatedProviderMsg, 10 | Read: resourceMetalPortRead, 11 | Schema: map[string]*schema.Schema{ 12 | "port_id": { 13 | Type: schema.TypeString, 14 | Optional: true, 15 | Description: "UUID of the port to lookup", 16 | ConflictsWith: []string{"device_id", "name"}, 17 | }, 18 | "device_id": { 19 | Type: schema.TypeString, 20 | Optional: true, 21 | Description: "Device UUID where to lookup the port", 22 | ConflictsWith: []string{"port_id"}, 23 | }, 24 | "name": { 25 | Type: schema.TypeString, 26 | Optional: true, 27 | Computed: true, 28 | Description: "Name of the port to look up, e.g. bond0, eth1", 29 | ConflictsWith: []string{"port_id"}, 30 | }, 31 | "network_type": { 32 | Type: schema.TypeString, 33 | Computed: true, 34 | Description: "One of " + NetworkTypeListHB, 35 | }, 36 | "type": { 37 | Type: schema.TypeString, 38 | Computed: true, 39 | Description: "Port type", 40 | }, 41 | "mac": { 42 | Type: schema.TypeString, 43 | Computed: true, 44 | Description: "MAC address of the port", 45 | }, 46 | "bond_id": { 47 | Type: schema.TypeString, 48 | Computed: true, 49 | Description: "UUID of the bond port", 50 | }, 51 | "bond_name": { 52 | Type: schema.TypeString, 53 | Computed: true, 54 | Description: "Name of the bond port", 55 | }, 56 | "bonded": { 57 | Type: schema.TypeBool, 58 | Computed: true, 59 | Description: "Flag indicating whether the port is bonded", 60 | }, 61 | "disbond_supported": { 62 | Type: schema.TypeBool, 63 | Computed: true, 64 | Description: "Flag indicating whether the port can be removed from a bond", 65 | }, 66 | "native_vlan_id": { 67 | Type: schema.TypeString, 68 | Computed: true, 69 | Description: "UUID of native VLAN of the port", 70 | }, 71 | "vlan_ids": { 72 | Type: schema.TypeList, 73 | Computed: true, 74 | Description: "UUIDs of attached VLANs", 75 | Elem: &schema.Schema{Type: schema.TypeString}, 76 | }, 77 | "vxlan_ids": { 78 | Type: schema.TypeList, 79 | Computed: true, 80 | Description: "UUIDs of attached VLANs", 81 | Elem: &schema.Schema{Type: schema.TypeInt}, 82 | }, 83 | "layer2": { 84 | Type: schema.TypeBool, 85 | Computed: true, 86 | Description: "Flag indicating whether the port is in layer2 (or layer3) mode", 87 | }, 88 | }, 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /metal/datasource_metal_port_test.go: -------------------------------------------------------------------------------- 1 | package metal 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" 8 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" 9 | ) 10 | 11 | func TestAccMetalPort_ByName(t *testing.T) { 12 | 13 | rs := acctest.RandString(10) 14 | 15 | resource.ParallelTest(t, resource.TestCase{ 16 | PreCheck: func() { testAccPreCheck(t) }, 17 | Providers: testAccProviders, 18 | Steps: []resource.TestStep{ 19 | { 20 | Config: testPortConfig_ByName(rs), 21 | Check: resource.ComposeTestCheckFunc( 22 | resource.TestCheckResourceAttr( 23 | "data.metal_port.test", "bond_name", "bond0"), 24 | ), 25 | }, 26 | }, 27 | }) 28 | } 29 | 30 | func testPortConfig_ByName(name string) string { 31 | return fmt.Sprintf(` 32 | %s 33 | 34 | resource "metal_project" "test" { 35 | name = "tfacc-pro-port-%s" 36 | } 37 | 38 | resource "metal_device" "test" { 39 | hostname = "tfacc-device-test-port" 40 | plan = local.plan 41 | metro = local.metro 42 | operating_system = "ubuntu_20_04" 43 | billing_cycle = "hourly" 44 | project_id = metal_project.test.id 45 | 46 | lifecycle { 47 | ignore_changes = [ 48 | plan, 49 | metro, 50 | ] 51 | } 52 | } 53 | 54 | data "metal_port" "test" { 55 | device_id = metal_device.test.id 56 | name = "eth0" 57 | }`, confAccMetalDevice_base(preferable_plans, preferable_metros), name) 58 | } 59 | 60 | func TestAccMetalPort_ById(t *testing.T) { 61 | 62 | rs := acctest.RandString(10) 63 | 64 | resource.ParallelTest(t, resource.TestCase{ 65 | PreCheck: func() { testAccPreCheck(t) }, 66 | Providers: testAccProviders, 67 | Steps: []resource.TestStep{ 68 | { 69 | Config: testPortConfig_ById(rs), 70 | Check: resource.ComposeTestCheckFunc( 71 | resource.TestCheckResourceAttrSet( 72 | "data.metal_port.test", "name"), 73 | ), 74 | }, 75 | }, 76 | }) 77 | } 78 | 79 | func testPortConfig_ById(name string) string { 80 | return fmt.Sprintf(` 81 | %s 82 | 83 | resource "metal_project" "test" { 84 | name = "tfacc-pro-port-%s" 85 | } 86 | 87 | resource "metal_device" "test" { 88 | hostname = "tfacc-device-test-port" 89 | plan = local.plan 90 | metro = local.metro 91 | operating_system = "ubuntu_20_04" 92 | billing_cycle = "hourly" 93 | project_id = metal_project.test.id 94 | 95 | lifecycle { 96 | ignore_changes = [ 97 | plan, 98 | metro, 99 | ] 100 | } 101 | } 102 | 103 | data "metal_port" "test" { 104 | port_id = metal_device.test.ports[0].id 105 | } 106 | 107 | `, confAccMetalDevice_base(preferable_plans, preferable_metros), name) 108 | } 109 | -------------------------------------------------------------------------------- /metal/resource_metal_project_ssh_key_test.go: -------------------------------------------------------------------------------- 1 | package metal 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" 8 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" 9 | "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" 10 | "github.com/packethost/packngo" 11 | ) 12 | 13 | func metalProjectSSHKeyConfig_Basic(name, publicSshKey string) string { 14 | return fmt.Sprintf(` 15 | %s 16 | 17 | resource "metal_project" "test" { 18 | name = "tfacc-pro-project_ssh_key-%s" 19 | } 20 | 21 | resource "metal_project_ssh_key" "test" { 22 | name = "tfacc-pro-key-test" 23 | public_key = "%s" 24 | project_id = "${metal_project.test.id}" 25 | } 26 | 27 | resource "metal_device" "test" { 28 | hostname = "tfacc-device-test-key" 29 | plan = local.plan 30 | facilities = local.facilities 31 | operating_system = "ubuntu_16_04" 32 | billing_cycle = "hourly" 33 | project_ssh_key_ids = ["${metal_project_ssh_key.test.id}"] 34 | project_id = "${metal_project.test.id}" 35 | 36 | lifecycle { 37 | ignore_changes = [ 38 | plan, 39 | facilities, 40 | ] 41 | } 42 | } 43 | 44 | `, confAccMetalDevice_base(preferable_plans, preferable_metros), name, publicSshKey) 45 | } 46 | 47 | func TestAccMetalProjectSSHKey_Basic(t *testing.T) { 48 | rs := acctest.RandString(10) 49 | var key packngo.SSHKey 50 | publicKeyMaterial, _, err := acctest.RandSSHKeyPair("") 51 | if err != nil { 52 | t.Fatalf("Cannot generate test SSH key pair: %s", err) 53 | } 54 | cfg := metalProjectSSHKeyConfig_Basic(rs, publicKeyMaterial) 55 | 56 | resource.ParallelTest(t, resource.TestCase{ 57 | PreCheck: func() { testAccPreCheck(t) }, 58 | Providers: testAccProviders, 59 | CheckDestroy: testAccCheckMetalProjectSSHKeyDestroy, 60 | Steps: []resource.TestStep{ 61 | { 62 | Config: cfg, 63 | Check: resource.ComposeTestCheckFunc( 64 | testAccCheckMetalSSHKeyExists("metal_project_ssh_key.test", &key), 65 | resource.TestCheckResourceAttr( 66 | "metal_project_ssh_key.test", "public_key", publicKeyMaterial), 67 | resource.TestCheckResourceAttrPair( 68 | "metal_device.test", "ssh_key_ids.0", 69 | "metal_project_ssh_key.test", "id", 70 | ), 71 | resource.TestCheckResourceAttrPair( 72 | "metal_project.test", "id", 73 | "metal_project_ssh_key.test", "project_id", 74 | ), 75 | ), 76 | }, 77 | }, 78 | }) 79 | } 80 | 81 | func testAccCheckMetalProjectSSHKeyDestroy(s *terraform.State) error { 82 | client := testAccProvider.Meta().(*packngo.Client) 83 | 84 | for _, rs := range s.RootModule().Resources { 85 | if rs.Type != "metal_project_ssh_key" { 86 | continue 87 | } 88 | if _, _, err := client.SSHKeys.Get(rs.Primary.ID, nil); err == nil { 89 | return fmt.Errorf("SSH key still exists") 90 | } 91 | } 92 | 93 | return nil 94 | } 95 | -------------------------------------------------------------------------------- /docs/resources/ip_attachment.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_title: "Equinix Metal: metal_ip_attachment" 3 | subcategory: "" 4 | description: |- 5 | Provides a Resource for Attaching IP Subnets from a Reserved Block to a Device 6 | --- 7 | 8 | # metal_ip_attachment (Resource) 9 | 10 | !> **PROVIDER DEPRECATED:** Equinix Metal Provider is now Deprecated. Please consider using [`equinix_metal_ip_attachment`](https://registry.terraform.io/providers/equinix/equinix/latest/docs/resources/equinix_metal_ip_attachment) resource from the [Equinix provider](https://registry.terraform.io/providers/equinix/equinix/latest/docs) instead of `metal_ip_attachment`. [See the Metal provider section for more details](../index.md#equinix-metal-provider) on the new provider and available migration guides. 11 | 12 | Provides a resource to attach elastic IP subnets to devices. 13 | 14 | To attach an IP subnet from a reserved block to a provisioned device, you must derive a subnet CIDR belonging to 15 | one of your reserved blocks in the same project and facility as the target device. 16 | 17 | For example, you have reserved IPv4 address block 147.229.10.152/30, you can choose to assign either the whole 18 | block as one subnet to a device; or 2 subnets with CIDRs 147.229.10.152/31' and 147.229.10.154/31; or 4 subnets 19 | with mask prefix length 32. More about the elastic IP subnets is [here](https://metal.equinix.com/developers/docs/networking/elastic-ips/). 20 | 21 | Device and reserved block must be in the same facility. 22 | 23 | ## Example Usage 24 | 25 | ```hcl 26 | # Reserve /30 block of max 2 public IPv4 addresses in Parsippany, NJ (ny5) for myproject 27 | resource "metal_reserved_ip_block" "myblock" { 28 | project_id = local.project_id 29 | facility = "ny5" 30 | quantity = 2 31 | } 32 | 33 | # Assign /32 subnet (single address) from reserved block to a device 34 | resource "metal_ip_attachment" "first_address_assignment" { 35 | device_id = metal_device.mydevice.id 36 | # following expression will result to sth like "147.229.10.152/32" 37 | cidr_notation = join("/", [cidrhost(metal_reserved_ip_block.myblock.cidr_notation, 0), "32"]) 38 | } 39 | ``` 40 | 41 | ## Argument Reference 42 | 43 | The following arguments are supported: 44 | 45 | * `device_id` - (Required) ID of device to which to assign the subnet 46 | * `cidr_notation` - (Required) CIDR notation of subnet from block reserved in the same 47 | project and facility as the device 48 | 49 | ## Attributes Reference 50 | 51 | The following attributes are exported: 52 | 53 | * `id` - The unique ID of the assignment 54 | * `device_id` - ID of device to which subnet is assigned 55 | * `cidr_notation` - Assigned subnet in CIDR notation, e.g. "147.229.15.30/31" 56 | * `gateway` - IP address of gateway for the subnet 57 | * `network` - Subnet network address 58 | * `netmask` - Subnet mask in decimal notation, e.g. "255.255.255.0" 59 | * `cidr` - length of CIDR prefix of the subnet as integer 60 | * `address_family` - Address family as integer (4 or 6) 61 | * `public` - boolean flag whether subnet is reachable from the Internet 62 | -------------------------------------------------------------------------------- /GNUmakefile: -------------------------------------------------------------------------------- 1 | ACCTEST_PARALLELISM ?= 8 2 | TEST?=$$(go list ./... |grep -v 'vendor') 3 | GOFMT_FILES?=$$(find . -name '*.go' |grep -v vendor) 4 | WEBSITE_REPO=github.com/hashicorp/terraform-website 5 | PKG_NAME=metal 6 | GOPATH?=$(HOME)/go 7 | 8 | default: build 9 | 10 | build: fmtcheck 11 | go install 12 | 13 | sweep: 14 | @echo "WARNING: This will destroy infrastructure. Use only in development accounts." 15 | go test $(TEST) -v -sweep=$(SWEEP) $(SWEEPARGS) 16 | 17 | test: fmtcheck 18 | go test $(TEST) -v $(TESTARGS) -timeout=30s -parallel=10 19 | 20 | testacc: fmtcheck 21 | TF_ACC=1 go test $(TEST) -v $(TESTARGS) -timeout=120m -parallel=$(ACCTEST_PARALLELISM) 22 | 23 | vet: 24 | @echo "go vet ." 25 | @go vet $$(go list ./... | grep -v vendor/) ; if [ $$? -eq 1 ]; then \ 26 | echo ""; \ 27 | echo "Vet found suspicious constructs. Please check the reported constructs"; \ 28 | echo "and fix them if necessary before submitting the code for review."; \ 29 | exit 1; \ 30 | fi 31 | 32 | fmt: 33 | gofmt -w $(GOFMT_FILES) 34 | 35 | fmtcheck: 36 | @sh -c "'$(CURDIR)/scripts/gofmtcheck.sh'" 37 | 38 | errcheck: 39 | @sh -c "'$(CURDIR)/scripts/errcheck.sh'" 40 | 41 | 42 | test-compile: 43 | @if [ "$(TEST)" = "./..." ]; then \ 44 | echo "ERROR: Set TEST to a specific package. For example,"; \ 45 | echo " make test-compile TEST=./$(PKG_NAME)"; \ 46 | exit 1; \ 47 | fi 48 | go test -c $(TEST) $(TESTARGS) 49 | 50 | docs-lint: 51 | @echo "==> Checking docs against linters..." 52 | @misspell -error -source=text docs/ || (echo; \ 53 | echo "Unexpected misspelling found in docs files."; \ 54 | echo "To automatically fix the misspelling, run 'make docs-lint-fix' and commit the changes."; \ 55 | exit 1) 56 | @docker run -v $(PWD):/markdown 06kellyjac/markdownlint-cli docs/ || (echo; \ 57 | echo "Unexpected issues found in docs Markdown files."; \ 58 | echo "To apply any automatic fixes, run 'make docs-lint-fix' and commit the changes."; \ 59 | exit 1) 60 | 61 | docs-lint-fix: 62 | @echo "==> Applying automatic docs linter fixes..." 63 | @misspell -w -source=text docs/ 64 | @docker run -v $(PWD):/markdown 06kellyjac/markdownlint-cli --fix docs/ 65 | 66 | tfproviderlint: 67 | @echo "==> Checking provider code against bflad/tfproviderlint..." 68 | @docker run -v $(PWD):/src bflad/tfproviderlint ./... || (echo; \ 69 | echo "Unexpected issues found in code with bflad/tfproviderlint."; \ 70 | echo "To apply automated fixes for check that support them, run 'make tfproviderlint-fix'."; \ 71 | exit 1) 72 | 73 | tfproviderlint-fix: 74 | @echo "==> Applying fixes with bflad/tfproviderlint..." 75 | @docker run -v $(PWD):/src bflad/tfproviderlint -fix ./... 76 | 77 | tfproviderdocs-check: 78 | @echo "==> Check provider docs with bflad/tfproviderdocs..." 79 | @docker run -v $(PWD):/src bflad/tfproviderdocs check -provider-name=metal || (echo; \ 80 | echo "Unexpected issues found in code with bflad/tfproviderdocs."; \ 81 | exit 1) 82 | 83 | .PHONY: build test testacc vet fmt fmtcheck errcheck test-compile docs-lint docs-lint-fix tfproviderlint tfproviderlint-fix tfproviderdocs-check 84 | 85 | -------------------------------------------------------------------------------- /docs/resources/project_ssh_key.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_title: "Equinix Metal: metal_project_ssh_key" 3 | subcategory: "" 4 | description: |- 5 | Provides an Equinix Metal Project SSH key resource. 6 | --- 7 | 8 | # metal_project_ssh_key (Resource) 9 | 10 | !> **PROVIDER DEPRECATED:** Equinix Metal Provider is now Deprecated. Please consider using [`equinix_metal_project_ssh_key`](https://registry.terraform.io/providers/equinix/equinix/latest/docs/resources/equinix_metal_project_ssh_key) resource from the [Equinix provider](https://registry.terraform.io/providers/equinix/equinix/latest/docs) instead of `metal_project_ssh_key`. [See the Metal provider section for more details](../index.md#equinix-metal-provider) on the new provider and available migration guides. 11 | 12 | Provides an Equinix Metal project SSH key resource to manage project-specific SSH keys. 13 | Project SSH keys will only be populated onto servers that belong to that project, in contrast to User SSH Keys. 14 | 15 | ## Example Usage 16 | 17 | ```hcl 18 | locals { 19 | project_id = "" 20 | } 21 | 22 | resource "metal_project_ssh_key" "test" { 23 | name = "test" 24 | public_key = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDM/unxJeFqxsTJcu6mhqsMHSaVlpu+Jj/P+44zrm6X/MAoHSX3X9oLgujEjjZ74yLfdfe0bJrbL2YgJzNaEkIQQ1VPMHB5EhTKUBGnzlPP0hHTnxsjAm9qDHgUPgvgFDQSAMzdJRJ0Cexo16Ph9VxCoLh3dxiE7s2gaM2FdVg7P8aSxKypsxAhYV3D0AwqzoOyT6WWhBoQ0xZ85XevOTnJCpImSemEGs6nVGEsWcEc1d1YvdxFjAK4SdsKUMkj4Dsy/leKsdi/DEAf356vbMT1UHsXXvy5TlHu/Pa6qF53v32Enz+nhKy7/8W2Yt2yWx8HnQcT2rug9lvCXagJO6oauqRTO77C4QZn13ZLMZgLT66S/tNh2EX0gi6vmIs5dth8uF+K6nxIyKJXbcA4ASg7F1OJrHKFZdTc5v1cPeq6PcbqGgc+8SrPYQmzvQqLoMBuxyos2hUkYOmw3aeWJj9nFa8Wu5WaN89mUeOqSkU4S5cgUzWUOmKey56B/j/s1sVys9rMhZapVs0wL4L9GBBM48N5jAQZnnpo85A8KsZq5ME22bTLqnxsDXqDYZvS7PSI6Dxi7eleOFE/NYYDkrgDLHTQri8ucDMVeVWHgoMY2bPXdn7KKy5jW5jKsf8EPARXg77A4gRYmgKrcwIKqJEUPqyxJBe0CPoGTqgXPRsUiQ== tomk@hp2" 25 | project_id = local.project_id 26 | } 27 | 28 | resource "metal_device" "test" { 29 | hostname = "test" 30 | plan = "c3.medium.x86" 31 | facilities = ["ny5"] 32 | operating_system = "ubuntu_20_04" 33 | billing_cycle = "hourly" 34 | project_ssh_key_ids = [metal_project_ssh_key.test.id] 35 | project_id = local.project_id 36 | } 37 | ``` 38 | 39 | ## Argument Reference 40 | 41 | The following arguments are supported: 42 | 43 | * `name` - (Required) The name of the SSH key for identification 44 | * `public_key` - (Required) The public key. If this is a file, it can be read using the file interpolation function 45 | * `project_id` - (Required) The ID of parent project 46 | 47 | ## Attributes Reference 48 | 49 | The following attributes are exported: 50 | 51 | * `id` - The unique ID of the key 52 | * `name` - The name of the SSH key 53 | * `public_key` - The text of the public key 54 | * `project_id` - The ID of parent project 55 | * `owner_id` - The ID of parent project (same as project_id) 56 | * `fingerprint` - The fingerprint of the SSH key 57 | * `created` - The timestamp for when the SSH key was created 58 | * `updated` - The timestamp for the last time the SSH key was updated 59 | -------------------------------------------------------------------------------- /docs/resources/connection.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_title: "Equinix Metal: connection" 3 | subcategory: "" 4 | description: |- 5 | Request/Create Equinix Fabric Connection 6 | --- 7 | 8 | # metal_connection (Resource) 9 | 10 | !> **PROVIDER DEPRECATED:** Equinix Metal Provider is now Deprecated. Please consider using [`equinix_metal_connection`](https://registry.terraform.io/providers/equinix/equinix/latest/docs/resources/equinix_metal_connection) resource from the [Equinix provider](https://registry.terraform.io/providers/equinix/equinix/latest/docs) instead of `metal_connection`. [See the Metal provider section for more details](../index.md#equinix-metal-provider) on the new provider and available migration guides. 11 | 12 | Use this resource to request of create an Interconnection from [Equinix Fabric - software-defined interconnections](https://metal.equinix.com/developers/docs/networking/fabric/) 13 | 14 | ~> Equinix Metal connection with service_token_type `a_side` is not generally available and may not be enabled yet for your organization. 15 | 16 | ## Example Usage 17 | 18 | ```hcl 19 | 20 | resource "metal_connection" "test" { 21 | name = "My Interconnection" 22 | project_id = metal_project.test.id 23 | type = "shared" 24 | redundancy = "redundant" 25 | metro = "sv" 26 | speed = "50Mbps" 27 | service_token_type = "a_side" 28 | } 29 | ``` 30 | 31 | ## Argument Reference 32 | 33 | * `name` - (Required) Name of the connection resource 34 | * `metro` - (Optional) Metro where the connection will be created 35 | * `facility` - (Optional) Facility where the connection will be created 36 | * `redundancy` - (Required) Connection redundancy - redundant or primary 37 | * `type` - (Required) Connection type - dedicated or shared 38 | * `project_id` - (Required) ID of the project where the connection is scoped to, must be set for shared connection 39 | * `speed` - (Required) Connection speed - one of 50Mbps, 200Mbps, 500Mbps, 1Gbps, 2Gbps, 5Gbps, 10Gbps 40 | * `description` - (Optional) Description for the connection resource 41 | * `mode` - (Optional) Mode for connections in IBX facilities with the dedicated type - standard or tunnel. Default is standard 42 | * `tags` - (Optional) String list of tags 43 | * `vlans` - (Optional) Only used with shared connection. Vlans to attach. Pass one vlan for Primary/Single connection and two vlans for Redundant connection 44 | * `service_token_type` - (Optional) Only used with shared connection. Type of service token to use for the connection, a_side or z_side 45 | 46 | ## Attributes Reference 47 | 48 | * `organization_id` - ID of the organization where the connection is scoped to 49 | * `status` - Status of the connection resource 50 | * `ports` - List of connection ports - primary (`ports[0]`) and secondary (`ports[1]`). Schema of port is described in documentation of the [metal_connection datasource](../data-sources/connection.md). 51 | * `service_tokens` - List of connection service tokens with attributes. Scehma of service_token is described in documentation of the [metal_connection datasource](../data-sources/connection.md). 52 | -------------------------------------------------------------------------------- /metal/resource_metal_ip_attachment.go: -------------------------------------------------------------------------------- 1 | package metal 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "path" 7 | 8 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" 9 | "github.com/packethost/packngo" 10 | ) 11 | 12 | func resourceMetalIPAttachment() *schema.Resource { 13 | ipAttachmentSchema := metalIPResourceComputedFields() 14 | ipAttachmentSchema["device_id"] = &schema.Schema{ 15 | Type: schema.TypeString, 16 | ForceNew: true, 17 | Required: true, 18 | } 19 | ipAttachmentSchema["cidr_notation"] = &schema.Schema{ 20 | Type: schema.TypeString, 21 | ForceNew: true, 22 | Required: true, 23 | } 24 | return &schema.Resource{ 25 | DeprecationMessage: deprecatedProviderMsg, 26 | Create: resourceMetalIPAttachmentCreate, 27 | Read: resourceMetalIPAttachmentRead, 28 | Delete: resourceMetalIPAttachmentDelete, 29 | Importer: &schema.ResourceImporter{ 30 | State: schema.ImportStatePassthrough, 31 | }, 32 | 33 | Schema: ipAttachmentSchema, 34 | } 35 | } 36 | 37 | func resourceMetalIPAttachmentCreate(d *schema.ResourceData, meta interface{}) error { 38 | client := meta.(*packngo.Client) 39 | deviceID := d.Get("device_id").(string) 40 | ipa := d.Get("cidr_notation").(string) 41 | 42 | req := packngo.AddressStruct{Address: ipa} 43 | 44 | assignment, _, err := client.DeviceIPs.Assign(deviceID, &req) 45 | if err != nil { 46 | return fmt.Errorf("error assigning address %s to device %s: %s", ipa, deviceID, err) 47 | } 48 | 49 | d.SetId(assignment.ID) 50 | 51 | return resourceMetalIPAttachmentRead(d, meta) 52 | } 53 | 54 | func resourceMetalIPAttachmentRead(d *schema.ResourceData, meta interface{}) error { 55 | client := meta.(*packngo.Client) 56 | assignment, _, err := client.DeviceIPs.Get(d.Id(), nil) 57 | if err != nil { 58 | err = friendlyError(err) 59 | 60 | // If the IP attachment was already destroyed, mark as succesfully gone. 61 | if isNotFound(err) { 62 | log.Printf("[WARN] IP attachment (%q) not found, removing from state", d.Id()) 63 | d.SetId("") 64 | return nil 65 | } 66 | return err 67 | } 68 | 69 | d.SetId(assignment.ID) 70 | d.Set("address", assignment.Address) 71 | d.Set("gateway", assignment.Gateway) 72 | d.Set("network", assignment.Network) 73 | d.Set("netmask", assignment.Netmask) 74 | d.Set("address_family", assignment.AddressFamily) 75 | d.Set("cidr", assignment.CIDR) 76 | d.Set("public", assignment.Public) 77 | d.Set("management", assignment.Management) 78 | d.Set("manageable", assignment.Manageable) 79 | 80 | d.Set("global", assignment.Global) 81 | 82 | d.Set("device_id", path.Base(assignment.AssignedTo.Href)) 83 | d.Set("cidr_notation", 84 | fmt.Sprintf("%s/%d", assignment.Network, assignment.CIDR)) 85 | 86 | return nil 87 | } 88 | 89 | func resourceMetalIPAttachmentDelete(d *schema.ResourceData, meta interface{}) error { 90 | client := meta.(*packngo.Client) 91 | 92 | resp, err := client.DeviceIPs.Unassign(d.Id()) 93 | if ignoreResponseErrors(httpForbidden, httpNotFound)(resp, err) != nil { 94 | return friendlyError(err) 95 | } 96 | 97 | d.SetId("") 98 | return nil 99 | } 100 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: "" 3 | page_title: "Provider: Equinix Metal" 4 | description: |- 5 | The Equinix Metal provider is used to interact with the Equinix Metal Host API. 6 | --- 7 | 8 | # Equinix Metal Provider 9 | 10 | !> **PROVIDER DEPRECATED:** Equinix Metal Provider is now Deprecated (End-of-Life scheduled for July 1, 2023) meaning that this software is only supported or maintained by Equinix Metal and its community in a case-by-case basis. The [Equinix provider](https://registry.terraform.io/providers/equinix/equinix/latest/docs) has full support for existing Terraform managed Metal resources once Terraform configuration and state are adapted. The Equinix provider manages resources including Network Edge and Fabric in addition to Metal. [Please review the Metal to Equinix provider migration guide](https://registry.terraform.io/providers/equinix/equinix/latest/docs/guides/migration_guide_equinix_metal). A guide is also available for [migrating from the Packet provider](https://registry.terraform.io/providers/equinix/equinix/latest/docs/guides/migration_guide_packet). 11 | 12 | The Equinix Metal (`metal`) provider is used to interact with the resources supported by [Equinix Metal](https://metal.equinix.com/). 13 | The provider needs to be configured with the proper credentials before it can be used. 14 | 15 | Use the navigation to the left to read about the available resources. 16 | 17 | ## Example Usage 18 | 19 | ```hcl 20 | terraform { 21 | required_providers { 22 | metal = { 23 | source = "equinix/metal" 24 | # version = "1.0.0" 25 | } 26 | } 27 | } 28 | 29 | # Configure the Equinix Metal Provider. 30 | provider "metal" { 31 | auth_token = var.auth_token 32 | } 33 | 34 | data "metal_project" "project" { 35 | name = "My Project" 36 | } 37 | 38 | # If you want to create a fresh project, you can create one with metal_project 39 | # 40 | # resource "metal_project" "cool_project" { 41 | # name = "My First Terraform Project" 42 | # } 43 | 44 | # Create a device and add it to tf_project_1 45 | resource "metal_device" "web1" { 46 | hostname = "web1" 47 | plan = "c3.medium.x86" 48 | metro = "ny" 49 | operating_system = "ubuntu_20_04" 50 | billing_cycle = "hourly" 51 | project_id = data.metal_project.project.id 52 | 53 | # if you created a project with the metal_project resource, refer to its ID 54 | # project_id = metal_project.cool_project.id 55 | 56 | # You can find the ID of your project in the URL of the Equinix Metal console. 57 | # For example, if you see your devices listed at 58 | # https://console.equinix.com/projects/352000fb2-ee46-4673-93a8-de2c2bdba33b 59 | # .. then 352000fb2-ee46-4673-93a8-de2c2bdba33b is your project ID. 60 | } 61 | ``` 62 | 63 | ## Argument Reference 64 | 65 | The following arguments are supported: 66 | 67 | * `auth_token` - (Required) This is your Equinix Metal API Auth token. This can 68 | also be specified with the `METAL_AUTH_TOKEN` environment variable. 69 | 70 | Use of the legacy `PACKET_AUTH_TOKEN` environment variable is deprecated. 71 | * `max_retries` - Maximum number of retries in case of network failure. 72 | * `max_retry_wait_seconds` - Maximum time to wait in case of network failure. 73 | -------------------------------------------------------------------------------- /docs/data-sources/spot_market_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_title: "Equinix Metal: metal_spot_market_request" 3 | subcategory: "" 4 | description: |- 5 | Provides a datasource for existing Spot Market Requests in the Equinix Metal host. 6 | --- 7 | 8 | # metal_spot_market_request (Data Source) 9 | 10 | !> **PROVIDER DEPRECATED:** Equinix Metal Provider is now Deprecated. Please consider using [`equinix_metal_spot_market_request`](https://registry.terraform.io/providers/equinix/equinix/latest/docs/data-sources/equinix_metal_spot_market_request) data source from the [Equinix provider](https://registry.terraform.io/providers/equinix/equinix/latest/docs) instead of `metal_spot_market_request`. [See the Metal provider section for more details](../index.md#equinix-metal-provider) on the new provider and available migration guides. 11 | 12 | Provides an Equinix Metal spot_market_request datasource. The datasource will contain list of device IDs created by referenced Spot Market Request. 13 | 14 | ## Example Usage 15 | 16 | ```hcl 17 | # Create a Spot Market Request, and print public IPv4 of the created devices, if any. 18 | 19 | resource "metal_spot_market_request" "req" { 20 | project_id = local.project_id 21 | max_bid_price = 0.1 22 | facilities = ["ny5"] 23 | devices_min = 2 24 | devices_max = 2 25 | wait_for_devices = true 26 | 27 | instance_parameters { 28 | hostname = "testspot" 29 | billing_cycle = "hourly" 30 | operating_system = "ubuntu_20_04" 31 | plan = "c3.small.x86" 32 | } 33 | } 34 | 35 | data "metal_spot_market_request" "dreq" { 36 | request_id = metal_spot_market_request.req.id 37 | } 38 | 39 | output "ids" { 40 | value = data.metal_spot_market_request.dreq.device_ids 41 | } 42 | 43 | data "metal_device" "devs" { 44 | count = length(data.metal_spot_market_request.dreq.device_ids) 45 | device_id = data.metal_spot_market_request.dreq.device_ids[count.index] 46 | } 47 | 48 | output "ips" { 49 | value = [for d in data.metal_device.devs : d.access_public_ipv4] 50 | } 51 | ``` 52 | 53 | With the code as `main.tf`, first create the spot market request: 54 | 55 | ``` 56 | terraform apply -target metal_spot_market_request.req 57 | ``` 58 | 59 | When the terraform run ends, run a full apply, and the IPv4 addresses will be printed: 60 | 61 | ``` 62 | $ terraform apply 63 | 64 | [...] 65 | 66 | ips = [ 67 | "947.85.199.231", 68 | "947.85.194.181", 69 | ] 70 | ``` 71 | 72 | ## Argument Reference 73 | 74 | The following arguments are supported: 75 | 76 | * `request_id` - (Required) The id of the Spot Market Request 77 | 78 | ## Attributes Reference 79 | 80 | The following attributes are exported: 81 | 82 | * `device_ids` - List of IDs of devices spawned by the referenced Spot Market Request 83 | * `devices_min` - Miniumum number devices to be created 84 | * `devices_max` - Maximum number devices to be created 85 | * `max_bid_price` - Maximum price user is willing to pay per hour per device 86 | * `facilities` - Facility IDs where devices should be created 87 | * `metro` - Metro where devices should be created. 88 | * `project_id` - Project ID 89 | * `plan` - The device plan slug. 90 | * `end_at` - Date and time When the spot market request will be ended. 91 | -------------------------------------------------------------------------------- /metal/resource_metal_volume.go: -------------------------------------------------------------------------------- 1 | package metal 2 | 3 | import ( 4 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" 5 | "github.com/packethost/packngo" 6 | ) 7 | 8 | func resourceMetalVolume() *schema.Resource { 9 | return &schema.Resource{ 10 | Create: removedResourceOp(resourceVolumeRemovedMsg), 11 | Read: removedResourceOp(resourceVolumeRemovedMsg), 12 | DeprecationMessage: "Volumes are deprecated, see https://metal.equinix.com/developers/docs/resilience-recovery/elastic-block-storage/#elastic-block-storage", 13 | Update: removedResourceOp(resourceVolumeRemovedMsg), 14 | Delete: resourceMetalVolumeDelete, 15 | Importer: &schema.ResourceImporter{ 16 | State: schema.ImportStatePassthrough, 17 | }, 18 | 19 | Schema: map[string]*schema.Schema{ 20 | "project_id": { 21 | Type: schema.TypeString, 22 | Required: true, 23 | ForceNew: true, 24 | }, 25 | 26 | "name": { 27 | Type: schema.TypeString, 28 | Computed: true, 29 | }, 30 | 31 | "description": { 32 | Type: schema.TypeString, 33 | Required: false, 34 | Optional: true, 35 | }, 36 | 37 | "size": { 38 | Type: schema.TypeInt, 39 | Required: true, 40 | }, 41 | 42 | "facility": { 43 | Type: schema.TypeString, 44 | Required: true, 45 | ForceNew: true, 46 | }, 47 | 48 | "plan": { 49 | Type: schema.TypeString, 50 | Required: true, 51 | }, 52 | 53 | "billing_cycle": { 54 | Type: schema.TypeString, 55 | Computed: true, 56 | Optional: true, 57 | }, 58 | 59 | "state": { 60 | Type: schema.TypeString, 61 | Computed: true, 62 | }, 63 | 64 | "locked": { 65 | Type: schema.TypeBool, 66 | Optional: true, 67 | }, 68 | 69 | "snapshot_policies": { 70 | Type: schema.TypeList, 71 | Optional: true, 72 | Elem: &schema.Resource{ 73 | Schema: map[string]*schema.Schema{ 74 | "snapshot_frequency": { 75 | Type: schema.TypeString, 76 | Required: true, 77 | ForceNew: true, 78 | }, 79 | "snapshot_count": { 80 | Type: schema.TypeInt, 81 | Required: true, 82 | ForceNew: true, 83 | }, 84 | }, 85 | }, 86 | }, 87 | 88 | "attachments": { 89 | Type: schema.TypeList, 90 | Computed: true, 91 | Elem: &schema.Resource{ 92 | Schema: map[string]*schema.Schema{ 93 | "href": { 94 | Type: schema.TypeString, 95 | Computed: true, 96 | }, 97 | }, 98 | }, 99 | }, 100 | 101 | "created": { 102 | Type: schema.TypeString, 103 | Computed: true, 104 | }, 105 | 106 | "updated": { 107 | Type: schema.TypeString, 108 | Computed: true, 109 | }, 110 | }, 111 | } 112 | } 113 | 114 | func resourceMetalVolumeDelete(d *schema.ResourceData, meta interface{}) error { 115 | client := meta.(*packngo.Client) 116 | 117 | resp, err := client.Volumes.Delete(d.Id()) 118 | if ignoreResponseErrors(httpForbidden, httpNotFound)(resp, err) != nil { 119 | return friendlyError(err) 120 | } 121 | 122 | return nil 123 | } 124 | -------------------------------------------------------------------------------- /docs/data-sources/virtual_circuit.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_title: "Equinix Metal: metal_virtual_circuit" 3 | subcategory: "" 4 | description: |- 5 | Retrieve Equinix Fabric Virtual Circuit 6 | --- 7 | 8 | # metal_virtual_circuit (Data Source) 9 | 10 | !> **PROVIDER DEPRECATED:** Equinix Metal Provider is now Deprecated. Please consider using [`equinix_metal_virtual_circuit`](https://registry.terraform.io/providers/equinix/equinix/latest/docs/data-sources/equinix_metal_virtual_circuit) data source from the [Equinix provider](https://registry.terraform.io/providers/equinix/equinix/latest/docs) instead of `metal_virtual_circuit`. [See the Metal provider section for more details](../index.md#equinix-metal-provider) on the new provider and available migration guides. 11 | 12 | Use this data source to retrieve a virtual circuit resource from [Equinix Fabric - software-defined interconnections](https://metal.equinix.com/developers/docs/networking/fabric/) 13 | 14 | -> **NOTE:** VRF features are not generally available. The interfaces related to VRF resources may change ahead of general availability. 15 | 16 | ## Example Usage 17 | 18 | ```hcl 19 | data "metal_connection" "example_connection" { 20 | connection_id = "4347e805-eb46-4699-9eb9-5c116e6a017d" 21 | } 22 | 23 | data "metal_virtual_circuit" "example_vc" { 24 | virtual_circuit_id = data.metal_connection.example_connection.ports[1].virtual_circuit_ids[0] 25 | } 26 | 27 | ``` 28 | 29 | ## Argument Reference 30 | 31 | The following arguments are supported: 32 | 33 | * `virtual_circuit_id` - (Required) ID of the virtual circuit resource 34 | 35 | ## Attributes Reference 36 | 37 | In addition to all arguments above, the following attributes are exported: 38 | 39 | * `name` - Name of the virtual circuit resource. 40 | * `status` - Status of the virtal circuit. 41 | * `port_id` - UUID of the Connection Port where the VC is scoped to. 42 | * `project_id` - ID of project to which the VC belongs. 43 | * `vnid`, `nni_vlan`, `nni_nvid` - VLAN parameters, see the [documentation for Equinix Fabric](https://metal.equinix.com/developers/docs/networking/fabric/). 44 | * `description` - Description for the Virtual Circuit resource. 45 | * `tags` - Tags for the Virtual Circuit resource. 46 | * `speed` - Speed of the Virtual Circuit resource. 47 | * `vrf_id` - UUID of the VLAN to associate. 48 | * `peer_asn` - The BGP ASN of the peer. The same ASN may be the used across several VCs, but it cannot be the same as the local_asn of the VRF. 49 | * `subnet` - A subnet from one of the IP 50 | blocks associated with the VRF that we will help create an IP reservation for. Can only be either a /30 or /31. 51 | * For a /31 block, it will only have two IP addresses, which will be used for 52 | the metal_ip and customer_ip. 53 | * For a /30 block, it will have four IP addresses, but the first and last IP addresses are not usable. We will default to the first usable IP address for the metal_ip. 54 | * `metal_ip` - The Metal IP address for the SVI (Switch Virtual Interface) of the VirtualCircuit. Will default to the first usable IP in the subnet. 55 | * `customer_ip` - The Customer IP address which the CSR switch will peer with. Will default to the other usable IP in the subnet. 56 | * `md5` - The password that can be set for the VRF BGP peer 57 | -------------------------------------------------------------------------------- /docs/data-sources/connection.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_title: "Equinix Metal: connection" 3 | subcategory: "" 4 | description: |- 5 | Retrieve Equinix Fabric Connection 6 | --- 7 | 8 | # metal_connection (Data Source) 9 | 10 | !> **PROVIDER DEPRECATED:** Equinix Metal Provider is now Deprecated. Please consider using [`equinix_metal_connection`](https://registry.terraform.io/providers/equinix/equinix/latest/docs/data-sources/equinix_metal_connection) data source from the [Equinix provider](https://registry.terraform.io/providers/equinix/equinix/latest/docs) instead of `metal_connection`. [See the Metal provider section for more details](../index.md#equinix-metal-provider) on the new provider and available migration guides. 11 | 12 | Use this data source to retrieve a connection resource from [Equinix Fabric - software-defined interconnections](https://metal.equinix.com/developers/docs/networking/fabric/) 13 | 14 | ~> Equinix Metal connection with service_token_type `a_side` is not generally available and may not be enabled yet for your organization. 15 | 16 | ## Example Usage 17 | 18 | ```hcl 19 | data "metal_connection" "example" { 20 | connection_id = "4347e805-eb46-4699-9eb9-5c116e6a017d" 21 | } 22 | ``` 23 | 24 | ## Argument Reference 25 | 26 | * `connection_id` - (Required) ID of the connection resource 27 | 28 | ## Attributes Reference 29 | 30 | * `name` - Name of the connection resource 31 | * `metro` - Slug of a metro to which the connection belongs 32 | * `facility` - Slug of a facility to which the connection belongs 33 | * `redundancy` - Connection redundancy, reduntant or primary 34 | * `type` - Connection type, dedicated or shared 35 | * `project_id` - ID of project to which the connection belongs 36 | * `speed` - Connection speed, one of 50Mbps, 200Mbps, 500Mbps, 1Gbps, 2Gbps, 5Gbps, 10Gbps 37 | * `description` - Description of the connection resource 38 | * `mode` - Mode for connections in IBX facilities with the dedicated type - standard or tunnel 39 | * `tags` - String list of tags 40 | * `vlans` - Attached VLANs. Only available in shared connection. One vlan for Primary/Single connection and two vlans for Redundant connection 41 | * `service_token_type` - Type of service token, a_side or z_side. One available in shared connection. 42 | * `organization_id` - ID of the organization where the connection is scoped to 43 | * `status` - Status of the connection resource 44 | * `service_tokens` - List of connection service tokens with attributes 45 | * `id` - UUID of the service token 46 | * `expires_at` - Expiration date of the service token 47 | * `max_allowed_speed` - Maximum allowed speed for the service token, string like in the `speed` attribute 48 | * `type` - Token type, `a_side` or `z_side` 49 | * `role` - Token role, `primary` or `secondary` 50 | * `ports` - List of connection ports - primary (`ports[0]`) and secondary (`ports[1]`) 51 | * `name` - Port name 52 | * `id` - Port UUID 53 | * `role` - Port role - primary or secondary 54 | * `speed` - Port speed in bits per second 55 | * `status` - Port status 56 | * `link_status` - Port link status 57 | * `virtual_circuit_ids` - List of IDs of virtual cicruits attached to this port 58 | * `token` - (Deprecated) Fabric Token from the [Equinix Fabric Portal](https://ecxfabric.equinix.com/dashboard) 59 | -------------------------------------------------------------------------------- /metal/datasource_metal_operating_system.go: -------------------------------------------------------------------------------- 1 | package metal 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | 7 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" 8 | "github.com/packethost/packngo" 9 | ) 10 | 11 | func dataSourceOperatingSystem() *schema.Resource { 12 | return &schema.Resource{ 13 | DeprecationMessage: deprecatedProviderMsg, 14 | Read: dataSourceMetalOperatingSystemRead, 15 | Schema: map[string]*schema.Schema{ 16 | "name": { 17 | Type: schema.TypeString, 18 | Description: "Name or part of the name of the distribution. Case insensitive", 19 | Optional: true, 20 | }, 21 | "distro": { 22 | Type: schema.TypeString, 23 | Description: "Name of the OS distribution", 24 | Optional: true, 25 | }, 26 | "version": { 27 | Type: schema.TypeString, 28 | Description: "Version of the distribution", 29 | Optional: true, 30 | }, 31 | "provisionable_on": { 32 | Type: schema.TypeString, 33 | Description: "Plan name", 34 | Optional: true, 35 | }, 36 | "slug": { 37 | Type: schema.TypeString, 38 | Description: "Operating system slug (same as id)", 39 | Computed: true, 40 | }, 41 | }, 42 | } 43 | } 44 | 45 | func dataSourceMetalOperatingSystemRead(d *schema.ResourceData, meta interface{}) error { 46 | client := meta.(*packngo.Client) 47 | 48 | name, nameOK := d.GetOk("name") 49 | distro, distroOK := d.GetOk("distro") 50 | version, versionOK := d.GetOk("version") 51 | provisionableOn, provisionableOnOK := d.GetOk("provisionable_on") 52 | 53 | if !nameOK && !distroOK && !versionOK && !provisionableOnOK { 54 | return fmt.Errorf("One of name, distro, version, or provisionable_on must be assigned") 55 | } 56 | 57 | oss, _, err := client.OperatingSystems.List() 58 | if err != nil { 59 | return err 60 | } 61 | 62 | if nameOK { 63 | temp := []packngo.OS{} 64 | for _, os := range oss { 65 | if strings.Contains(strings.ToLower(os.Name), strings.ToLower(name.(string))) { 66 | temp = append(temp, os) 67 | } 68 | } 69 | oss = temp 70 | } 71 | 72 | if distroOK && (len(oss) != 0) { 73 | temp := []packngo.OS{} 74 | for _, v := range oss { 75 | if v.Distro == distro.(string) { 76 | temp = append(temp, v) 77 | } 78 | } 79 | oss = temp 80 | } 81 | 82 | if versionOK && (len(oss) != 0) { 83 | temp := []packngo.OS{} 84 | for _, v := range oss { 85 | if v.Version == version.(string) { 86 | temp = append(temp, v) 87 | } 88 | } 89 | oss = temp 90 | } 91 | 92 | if provisionableOnOK && (len(oss) != 0) { 93 | temp := []packngo.OS{} 94 | for _, v := range oss { 95 | for _, po := range v.ProvisionableOn { 96 | if po == provisionableOn.(string) { 97 | temp = append(temp, v) 98 | } 99 | } 100 | } 101 | oss = temp 102 | } 103 | 104 | if len(oss) == 0 { 105 | return fmt.Errorf("There are no operating systems that match the search criteria") 106 | } 107 | 108 | if len(oss) > 1 { 109 | return fmt.Errorf("There is more than one operating system that matches the search criteria") 110 | } 111 | d.Set("name", oss[0].Name) 112 | d.Set("distro", oss[0].Distro) 113 | d.Set("version", oss[0].Version) 114 | d.Set("slug", oss[0].Slug) 115 | d.SetId(oss[0].Slug) 116 | return nil 117 | } 118 | -------------------------------------------------------------------------------- /metal/datasource_metal_facility_test.go: -------------------------------------------------------------------------------- 1 | package metal 2 | 3 | import ( 4 | "fmt" 5 | "regexp" 6 | "testing" 7 | 8 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" 9 | ) 10 | 11 | var ( 12 | matchErrMissingFeature = regexp.MustCompile(`.*doesn't have feature.*`) 13 | matchErrNoCapacity = regexp.MustCompile(`Not enough capacity.*`) 14 | ) 15 | 16 | func TestAccDataSourceFacility_Basic(t *testing.T) { 17 | testFac := "da11" 18 | 19 | resource.ParallelTest(t, resource.TestCase{ 20 | PreCheck: func() { testAccPreCheck(t) }, 21 | Providers: testAccProviders, 22 | Steps: []resource.TestStep{ 23 | { 24 | Config: testAccDataSourceFacilityConfigBasic(testFac), 25 | Check: resource.ComposeTestCheckFunc( 26 | resource.TestCheckResourceAttr( 27 | "data.metal_facility.test", "code", testFac), 28 | ), 29 | }, 30 | { 31 | Config: testAccDataSourceFacilityConfigCapacityReasonable(testFac), 32 | Check: resource.ComposeTestCheckFunc( 33 | resource.TestCheckResourceAttr( 34 | "data.metal_facility.test", "code", testFac), 35 | ), 36 | }, 37 | { 38 | Config: testAccDataSourceFacilityConfigCapacityUnreasonable(testFac), 39 | ExpectError: matchErrNoCapacity, 40 | }, 41 | { 42 | Config: testAccDataSourceFacilityConfigCapacityUnreasonableMultiple(testFac), 43 | ExpectError: matchErrNoCapacity, 44 | }, 45 | }, 46 | }) 47 | } 48 | 49 | func TestAccDataSourceFacility_Features(t *testing.T) { 50 | resource.ParallelTest(t, resource.TestCase{ 51 | PreCheck: func() { testAccPreCheck(t) }, 52 | Providers: testAccProviders, 53 | Steps: []resource.TestStep{ 54 | { 55 | Config: testAccDataSourceFacilityConfigFeatures(), 56 | ExpectError: matchErrMissingFeature, 57 | }, 58 | }, 59 | }) 60 | } 61 | 62 | func testAccDataSourceFacilityConfigFeatures() string { 63 | return ` 64 | data "metal_facility" "test" { 65 | code = "da11" 66 | features_required = ["baremetal", "ibx", "missingFeature"] 67 | } 68 | ` 69 | } 70 | 71 | func testAccDataSourceFacilityConfigBasic(facCode string) string { 72 | return fmt.Sprintf(` 73 | data "metal_facility" "test" { 74 | code = "%s" 75 | } 76 | `, facCode) 77 | } 78 | 79 | func testAccDataSourceFacilityConfigCapacityUnreasonable(facCode string) string { 80 | return fmt.Sprintf(` 81 | data "metal_facility" "test" { 82 | code = "%s" 83 | capacity { 84 | plan = "c3.small.x86" 85 | quantity = 1000 86 | } 87 | } 88 | `, facCode) 89 | } 90 | 91 | func testAccDataSourceFacilityConfigCapacityReasonable(facCode string) string { 92 | return fmt.Sprintf(` 93 | data "metal_facility" "test" { 94 | code = "%s" 95 | capacity { 96 | plan = "c3.small.x86" 97 | quantity = 1 98 | } 99 | capacity { 100 | plan = "c3.medium.x86" 101 | quantity = 1 102 | } 103 | } 104 | `, facCode) 105 | } 106 | 107 | func testAccDataSourceFacilityConfigCapacityUnreasonableMultiple(facCode string) string { 108 | return fmt.Sprintf(` 109 | data "metal_facility" "test" { 110 | code = "%s" 111 | capacity { 112 | plan = "c3.small.x86" 113 | quantity = 1 114 | } 115 | capacity { 116 | plan = "c3.medium.x86" 117 | quantity = 1000 118 | } 119 | } 120 | `, facCode) 121 | } 122 | -------------------------------------------------------------------------------- /metal/datasource_metal_spot_market_request_test.go: -------------------------------------------------------------------------------- 1 | package metal 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" 8 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" 9 | "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" 10 | "github.com/packethost/packngo" 11 | ) 12 | 13 | func TestAccDataSourceMetalSpotMarketRequest_Basic(t *testing.T) { 14 | projectName := fmt.Sprintf("ds-device-%s", acctest.RandString(10)) 15 | var ( 16 | facKey packngo.SpotMarketRequest 17 | metKey packngo.SpotMarketRequest 18 | ) 19 | resource.ParallelTest(t, resource.TestCase{ 20 | PreCheck: func() { testAccPreCheck(t) }, 21 | Providers: testAccProviders, 22 | CheckDestroy: testAccCheckMetalSpotMarketRequestDestroy, 23 | Steps: []resource.TestStep{ 24 | { 25 | Config: testDataSourceMetalSpotMarketRequestConfig_Basic(projectName), 26 | Check: resource.ComposeTestCheckFunc( 27 | testAccCheckMetalSpotMarketRequestExists("metal_spot_market_request.req", &facKey), 28 | ), 29 | }, 30 | { 31 | Config: testDataSourceMetalSpotMarketRequestConfig_Metro(projectName), 32 | PlanOnly: true, 33 | ExpectNonEmptyPlan: true, 34 | }, 35 | { 36 | Config: testDataSourceMetalSpotMarketRequestConfig_Metro(projectName), 37 | Check: resource.ComposeTestCheckFunc( 38 | testAccCheckMetalSpotMarketRequestExists("metal_spot_market_request.req", &metKey), 39 | func(_ *terraform.State) error { 40 | if metKey.ID == facKey.ID { 41 | return fmt.Errorf("Expected a new spot_market_request") 42 | } 43 | return nil 44 | }, 45 | ), 46 | }, 47 | }, 48 | }) 49 | } 50 | 51 | func testDataSourceMetalSpotMarketRequestConfig_Basic(projSuffix string) string { 52 | return fmt.Sprintf(` 53 | 54 | resource "metal_project" "test" { 55 | name = "tfacc-pro-spot_market_request-%s" 56 | } 57 | 58 | resource "metal_spot_market_request" "req" { 59 | project_id = "${metal_project.test.id}" 60 | max_bid_price = 0.01 61 | facilities = ["da11"] 62 | devices_min = 1 63 | devices_max = 1 64 | wait_for_devices = false 65 | 66 | instance_parameters { 67 | hostname = "tfacc-spot-test" 68 | billing_cycle = "hourly" 69 | operating_system = "ubuntu_20_04" 70 | plan = "c3.medium.x86" 71 | } 72 | } 73 | 74 | data "metal_spot_market_request" "dreq" { 75 | request_id = metal_spot_market_request.req.id 76 | } 77 | `, projSuffix) 78 | } 79 | 80 | func testDataSourceMetalSpotMarketRequestConfig_Metro(projSuffix string) string { 81 | return fmt.Sprintf(` 82 | 83 | resource "metal_project" "test" { 84 | name = "tfacc-pro-spot_market_request-%s" 85 | } 86 | 87 | resource "metal_spot_market_request" "req" { 88 | project_id = "${metal_project.test.id}" 89 | max_bid_price = 0.01 90 | metro = "da" 91 | devices_min = 1 92 | devices_max = 1 93 | wait_for_devices = false 94 | 95 | instance_parameters { 96 | hostname = "tfacc-spot-test" 97 | billing_cycle = "hourly" 98 | operating_system = "ubuntu_20_04" 99 | plan = "c3.medium.x86" 100 | } 101 | } 102 | 103 | data "metal_spot_market_request" "dreq" { 104 | request_id = metal_spot_market_request.req.id 105 | } 106 | `, projSuffix) 107 | } 108 | -------------------------------------------------------------------------------- /metal/resource_metal_bgp_session.go: -------------------------------------------------------------------------------- 1 | package metal 2 | 3 | import ( 4 | "log" 5 | 6 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" 7 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" 8 | "github.com/packethost/packngo" 9 | ) 10 | 11 | func resourceMetalBGPSession() *schema.Resource { 12 | return &schema.Resource{ 13 | DeprecationMessage: deprecatedProviderMsg, 14 | Create: resourceMetalBGPSessionCreate, 15 | Read: resourceMetalBGPSessionRead, 16 | Delete: resourceMetalBGPSessionDelete, 17 | Importer: &schema.ResourceImporter{ 18 | State: schema.ImportStatePassthrough, 19 | }, 20 | 21 | Schema: map[string]*schema.Schema{ 22 | "device_id": { 23 | Type: schema.TypeString, 24 | Description: "ID of device", 25 | Required: true, 26 | ForceNew: true, 27 | }, 28 | "address_family": { 29 | Type: schema.TypeString, 30 | Description: "ipv4 or ipv6", 31 | Required: true, 32 | ForceNew: true, 33 | ValidateFunc: validation.StringInSlice([]string{"ipv4", "ipv6"}, false), 34 | }, 35 | "default_route": { 36 | Type: schema.TypeBool, 37 | Description: "Boolean flag to set the default route policy. False by default", 38 | Optional: true, 39 | Default: false, 40 | ForceNew: true, 41 | }, 42 | 43 | "status": { 44 | Type: schema.TypeString, 45 | Description: "Status of the session - up or down", 46 | Computed: true, 47 | }, 48 | }, 49 | } 50 | } 51 | 52 | func resourceMetalBGPSessionCreate(d *schema.ResourceData, meta interface{}) error { 53 | client := meta.(*packngo.Client) 54 | dID := d.Get("device_id").(string) 55 | addressFamily := d.Get("address_family").(string) 56 | defaultRoute := d.Get("default_route").(bool) 57 | log.Printf("[DEBUG] creating %s BGP session to device (%s)\n", addressFamily, dID) 58 | bgpSession, _, err := client.BGPSessions.Create( 59 | dID, packngo.CreateBGPSessionRequest{ 60 | AddressFamily: addressFamily, 61 | DefaultRoute: &defaultRoute}) 62 | if err != nil { 63 | return friendlyError(err) 64 | } 65 | 66 | d.SetId(bgpSession.ID) 67 | return resourceMetalBGPSessionRead(d, meta) 68 | } 69 | 70 | func resourceMetalBGPSessionRead(d *schema.ResourceData, meta interface{}) error { 71 | client := meta.(*packngo.Client) 72 | bgpSession, _, err := client.BGPSessions.Get(d.Id(), 73 | &packngo.GetOptions{Includes: []string{"device"}}) 74 | if err != nil { 75 | err = friendlyError(err) 76 | if isNotFound(err) { 77 | log.Printf("[WARN] BGP Session (%s) not found, removing from state", d.Id()) 78 | 79 | d.SetId("") 80 | return nil 81 | } 82 | return err 83 | } 84 | defaultRoute := false 85 | if bgpSession.DefaultRoute != nil { 86 | if *(bgpSession.DefaultRoute) { 87 | defaultRoute = true 88 | } 89 | } 90 | d.Set("device_id", bgpSession.Device.ID) 91 | d.Set("address_family", bgpSession.AddressFamily) 92 | d.Set("status", bgpSession.Status) 93 | d.Set("default_route", defaultRoute) 94 | d.SetId(bgpSession.ID) 95 | return nil 96 | } 97 | 98 | func resourceMetalBGPSessionDelete(d *schema.ResourceData, meta interface{}) error { 99 | client := meta.(*packngo.Client) 100 | resp, err := client.BGPSessions.Delete(d.Id()) 101 | return ignoreResponseErrors(httpForbidden, httpNotFound)(resp, err) 102 | } 103 | -------------------------------------------------------------------------------- /metal/config.go: -------------------------------------------------------------------------------- 1 | package metal 2 | 3 | import ( 4 | "context" 5 | "crypto/x509" 6 | "fmt" 7 | "log" 8 | "net/http" 9 | "net/http/httputil" 10 | "net/url" 11 | "os" 12 | "regexp" 13 | "strings" 14 | "time" 15 | 16 | "github.com/equinix/terraform-provider-metal/version" 17 | "github.com/hashicorp/go-retryablehttp" 18 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/logging" 19 | "github.com/hashicorp/terraform-plugin-sdk/v2/meta" 20 | "github.com/packethost/packngo" 21 | ) 22 | 23 | type DumpTransport struct { 24 | r http.RoundTripper 25 | } 26 | 27 | func (d *DumpTransport) RoundTrip(h *http.Request) (*http.Response, error) { 28 | dump, _ := httputil.DumpRequestOut(h, true) 29 | fmt.Printf("****REQUEST****\n%q\n", dump) 30 | resp, err := d.r.RoundTrip(h) 31 | dump, _ = httputil.DumpResponse(resp, true) 32 | fmt.Printf("****RESPONSE****\n%q\n****************\n\n", dump) 33 | return resp, err 34 | } 35 | 36 | const ( 37 | consumerToken = "aZ9GmqHTPtxevvFq9SK3Pi2yr9YCbRzduCSXF2SNem5sjB91mDq7Th3ZwTtRqMWZ" 38 | uaEnvVar = "TF_APPEND_USER_AGENT" 39 | ) 40 | 41 | type Config struct { 42 | terraformVersion string 43 | AuthToken string 44 | MaxRetries int 45 | MaxRetryWait time.Duration 46 | } 47 | 48 | var redirectsErrorRe = regexp.MustCompile(`stopped after \d+ redirects\z`) 49 | 50 | func MetalRetryPolicy(ctx context.Context, resp *http.Response, err error) (bool, error) { 51 | if ctx.Err() != nil { 52 | return false, ctx.Err() 53 | } 54 | 55 | if err != nil { 56 | if v, ok := err.(*url.Error); ok { 57 | // Don't retry if the error was due to too many redirects. 58 | if redirectsErrorRe.MatchString(v.Error()) { 59 | return false, nil 60 | } 61 | 62 | // Don't retry if the error was due to TLS cert verification failure. 63 | if _, ok := v.Err.(x509.UnknownAuthorityError); ok { 64 | return false, nil 65 | } 66 | } 67 | 68 | // The error is likely recoverable so retry. 69 | return true, nil 70 | } 71 | return false, nil 72 | } 73 | 74 | func terraformUserAgent(version string) string { 75 | ua := fmt.Sprintf("HashiCorp Terraform/%s (+https://www.terraform.io) Terraform Plugin SDK/%s", 76 | version, meta.SDKVersionString()) 77 | 78 | if add := os.Getenv(uaEnvVar); add != "" { 79 | add = strings.TrimSpace(add) 80 | if len(add) > 0 { 81 | ua += " " + add 82 | log.Printf("[DEBUG] Using modified User-Agent: %s", ua) 83 | } 84 | } 85 | 86 | return ua 87 | } 88 | 89 | // Client returns a new client for accessing Equinix Metal's API. 90 | func (c *Config) Client() *packngo.Client { 91 | transport := http.DefaultTransport 92 | // transport = &DumpTransport{http.DefaultTransport} // Debug only 93 | transport = logging.NewTransport("Equinix Metal", transport) 94 | retryClient := retryablehttp.NewClient() 95 | retryClient.HTTPClient.Transport = transport 96 | retryClient.RetryMax = c.MaxRetries 97 | retryClient.RetryWaitMin = time.Second 98 | retryClient.RetryWaitMax = c.MaxRetryWait 99 | retryClient.CheckRetry = MetalRetryPolicy 100 | standardClient := retryClient.StandardClient() 101 | 102 | client := packngo.NewClientWithAuth(consumerToken, c.AuthToken, standardClient) 103 | tfUserAgent := terraformUserAgent(c.terraformVersion) 104 | userAgent := fmt.Sprintf("%s terraform-provider-metal/%s %s", 105 | tfUserAgent, version.ProviderVersion, client.UserAgent) 106 | 107 | client.UserAgent = strings.TrimSpace(userAgent) 108 | 109 | return client 110 | } 111 | -------------------------------------------------------------------------------- /metal/resource_metal_bgp_setup_test.go: -------------------------------------------------------------------------------- 1 | package metal 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" 8 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" 9 | "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" 10 | "github.com/packethost/packngo" 11 | ) 12 | 13 | func TestAccMetalBGPSetup_Basic(t *testing.T) { 14 | rs := acctest.RandString(10) 15 | 16 | resource.ParallelTest(t, resource.TestCase{ 17 | PreCheck: func() { testAccPreCheck(t) }, 18 | Providers: testAccProviders, 19 | CheckDestroy: testAccCheckMetalBGPSetupDestroy, 20 | Steps: []resource.TestStep{ 21 | { 22 | Config: testAccCheckMetalBGPSetupConfig_basic(rs), 23 | Check: resource.ComposeTestCheckFunc( 24 | resource.TestCheckResourceAttrPair( 25 | "metal_device.test", "id", 26 | "metal_bgp_session.test4", "device_id"), 27 | resource.TestCheckResourceAttrPair( 28 | "metal_device.test", "id", 29 | "metal_bgp_session.test6", "device_id"), 30 | resource.TestCheckResourceAttr( 31 | "metal_bgp_session.test4", "default_route", "true"), 32 | resource.TestCheckResourceAttr( 33 | "metal_bgp_session.test6", "default_route", "true"), 34 | resource.TestCheckResourceAttr( 35 | "metal_bgp_session.test4", "address_family", "ipv4"), 36 | resource.TestCheckResourceAttr( 37 | "metal_bgp_session.test6", "address_family", "ipv6"), 38 | // there will be 2 BGP neighbors, for IPv4 and IPv6 39 | resource.TestCheckResourceAttr( 40 | "data.metal_device_bgp_neighbors.test", "bgp_neighbors.#", "2"), 41 | ), 42 | }, 43 | { 44 | ResourceName: "metal_bgp_session.test4", 45 | ImportState: true, 46 | ImportStateVerify: true, 47 | }, 48 | }, 49 | }) 50 | } 51 | 52 | func testAccCheckMetalBGPSetupDestroy(s *terraform.State) error { 53 | client := testAccProvider.Meta().(*packngo.Client) 54 | 55 | for _, rs := range s.RootModule().Resources { 56 | if rs.Type != "metal_bgp_session" { 57 | continue 58 | } 59 | if _, _, err := client.BGPSessions.Get(rs.Primary.ID, nil); err == nil { 60 | return fmt.Errorf("BGPSession still exists") 61 | } 62 | } 63 | 64 | return nil 65 | } 66 | 67 | func testAccCheckMetalBGPSetupConfig_basic(name string) string { 68 | return fmt.Sprintf(` 69 | %s 70 | 71 | resource "metal_project" "test" { 72 | name = "tfacc-pro-bgp_session-%s" 73 | bgp_config { 74 | deployment_type = "local" 75 | md5 = "C179c28c41a85b" 76 | asn = 65000 77 | } 78 | } 79 | 80 | resource "metal_device" "test" { 81 | hostname = "tfacc-test-bgp-sesh" 82 | plan = local.plan 83 | facilities = local.facilities 84 | operating_system = "ubuntu_16_04" 85 | billing_cycle = "hourly" 86 | project_id = "${metal_project.test.id}" 87 | 88 | lifecycle { 89 | ignore_changes = [ 90 | plan, 91 | facilities, 92 | ] 93 | } 94 | } 95 | 96 | resource "metal_bgp_session" "test4" { 97 | device_id = "${metal_device.test.id}" 98 | address_family = "ipv4" 99 | default_route = true 100 | } 101 | 102 | resource "metal_bgp_session" "test6" { 103 | device_id = "${metal_device.test.id}" 104 | address_family = "ipv6" 105 | default_route = true 106 | } 107 | 108 | data "metal_device_bgp_neighbors" "test" { 109 | device_id = metal_bgp_session.test4.device_id 110 | } 111 | `, confAccMetalDevice_base(preferable_plans, preferable_metros), name) 112 | } 113 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at support@equinixmetal.com. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /metal/internal/datalist/values.go: -------------------------------------------------------------------------------- 1 | package datalist 2 | 3 | import ( 4 | "math" 5 | "regexp" 6 | "strings" 7 | 8 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" 9 | ) 10 | 11 | func floatApproxEquals(a, b float64) bool { 12 | return math.Abs(a-b) < 0.000001 13 | } 14 | 15 | func valueMatches(s *schema.Schema, value interface{}, filterValue interface{}, matchBy string) bool { 16 | switch s.Type { 17 | case schema.TypeString: 18 | switch matchBy { 19 | case "substring": 20 | return strings.Contains(value.(string), filterValue.(string)) 21 | case "re": 22 | return filterValue.(*regexp.Regexp).MatchString(value.(string)) 23 | } 24 | return strings.EqualFold(filterValue.(string), value.(string)) 25 | 26 | case schema.TypeBool: 27 | return filterValue.(bool) == value.(bool) 28 | 29 | case schema.TypeInt: 30 | val := value.(int) 31 | filter := filterValue.(int) 32 | switch matchBy { 33 | case "less_than": 34 | return val < filter 35 | case "less_than_or_equal": 36 | return val <= filter 37 | case "greater_than": 38 | return val > filter 39 | case "greater_than_or_equal": 40 | return val >= filter 41 | } 42 | return val == filter 43 | 44 | case schema.TypeFloat: 45 | val := value.(float64) 46 | filter := filterValue.(float64) 47 | switch matchBy { 48 | case "less_than": 49 | return val != 0. && (val < filter) 50 | case "less_than_or_equal": 51 | return val != 0. && ((val < filter) || floatApproxEquals(filter, val)) 52 | case "greater_than": 53 | return val != 0. && (val > filter) 54 | case "greater_than_or_equal": 55 | return val != 0. && ((val > filter) || floatApproxEquals(filter, val)) 56 | } 57 | return floatApproxEquals(filter, val) 58 | 59 | case schema.TypeList: 60 | listValues := value.([]interface{}) 61 | result := false 62 | for _, listValue := range listValues { 63 | valueDoesMatch := valueMatches(s.Elem.(*schema.Schema), listValue, filterValue, matchBy) 64 | result = result || valueDoesMatch 65 | } 66 | return result 67 | 68 | case schema.TypeSet: 69 | setValue := value.(*schema.Set) 70 | listValues := setValue.List() 71 | result := false 72 | for _, listValue := range listValues { 73 | valueDoesMatch := valueMatches(s.Elem.(*schema.Schema), listValue, filterValue, matchBy) 74 | result = result || valueDoesMatch 75 | } 76 | return result 77 | } 78 | 79 | return false 80 | } 81 | 82 | func compareValues(s *schema.Schema, value1 interface{}, value2 interface{}) int { 83 | switch s.Type { 84 | case schema.TypeString: 85 | return strings.Compare(value1.(string), value2.(string)) 86 | 87 | case schema.TypeBool: 88 | boolValue1 := value1.(bool) 89 | boolValue2 := value2.(bool) 90 | if boolValue1 == boolValue2 { 91 | return 0 92 | } else if !boolValue1 { 93 | return -1 94 | } else { 95 | return 1 96 | } 97 | 98 | case schema.TypeInt: 99 | intValue1 := value1.(int) 100 | intValue2 := value2.(int) 101 | if intValue1 < intValue2 { 102 | return -1 103 | } else if intValue1 > intValue2 { 104 | return 1 105 | } else { 106 | return 0 107 | } 108 | 109 | case schema.TypeFloat: 110 | floatValue1 := value1.(float64) 111 | floatValue2 := value2.(float64) 112 | if floatApproxEquals(floatValue1, floatValue2) { 113 | return 0 114 | } else if floatValue1 < floatValue2 { 115 | return -1 116 | } else if floatValue1 > floatValue2 { 117 | return 1 118 | } else { 119 | return 0 120 | } 121 | 122 | default: 123 | panic("Illegal state: Unsupported value type for sort") 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /docs/resources/spot_market_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_title: "Equinix Metal: metal_spot_market_request" 3 | subcategory: "" 4 | description: |- 5 | Provides an Equinix Metal Spot Market Request Resource. 6 | --- 7 | 8 | # metal_spot_market_request (Resource) 9 | 10 | !> **PROVIDER DEPRECATED:** Equinix Metal Provider is now Deprecated. Please consider using [`equinix_metal_spot_market_request`](https://registry.terraform.io/providers/equinix/equinix/latest/docs/resources/equinix_metal_spot_market_request) resource from the [Equinix provider](https://registry.terraform.io/providers/equinix/equinix/latest/docs) instead of `metal_spot_market_request`. [See the Metal provider section for more details](../index.md#equinix-metal-provider) on the new provider and available migration guides. 11 | 12 | Provides an Equinix Metal Spot Market Request resource to allow you to 13 | manage spot market requests on your account. For more detail on Spot Market, see [this article in Equinix Metal documentation](https://metal.equinix.com/developers/docs/deploy/spot-market/). 14 | 15 | ## Example Usage 16 | 17 | ```hcl 18 | # Create a spot market request 19 | resource "metal_spot_market_request" "req" { 20 | project_id = local.project_id 21 | max_bid_price = 0.03 22 | facilities = ["ny5"] 23 | devices_min = 1 24 | devices_max = 1 25 | 26 | instance_parameters { 27 | hostname = "testspot" 28 | billing_cycle = "hourly" 29 | operating_system = "ubuntu_20_04" 30 | plan = "c3.small.x86" 31 | } 32 | } 33 | ``` 34 | 35 | ## Argument Reference 36 | 37 | The following arguments are supported: 38 | 39 | * `devices_max` - (Required) Maximum number devices to be created 40 | * `devices_min` - (Required) Miniumum number devices to be created 41 | * `max_bid_price` - (Required) Maximum price user is willing to pay per hour per device 42 | * `project_id` - (Required) Project ID 43 | * `wait_for_devices` - (Optional) On resource creation - wait until all desired devices are active, on resource destruction - wait until devices are removed 44 | * `facilities` - (Optional) Facility IDs where devices should be created 45 | * `metro` - (Optional) Metro where devices should be created 46 | * `locked` - (Optional) Blocks deletion of the SpotMarketRequest device until the lock is disabled 47 | * `instance_parameters` - (Required) Parameters for devices provisioned from this request. You can find the parameter description from the [metal_device doc](device.md). 48 | * `billing_cycle` 49 | * `plan` 50 | * `operating_system` 51 | * `hostname` 52 | * `termintation_time` 53 | * `always_pxe` 54 | * `description` 55 | * `features` 56 | * `locked` 57 | * `project_ssh_keys` 58 | * `user_ssh_keys` 59 | * `userdata` 60 | * `customdata` 61 | * `ipxe_script_url` 62 | * `tags` 63 | 64 | ### Timeouts 65 | 66 | The `timeouts` block allows you to specify [timeouts](https://www.terraform.io/configuration/resources#operation-timeouts) for certain actions: 67 | 68 | * `create` - (Defaults to 60 mins) Used when creating the Spot Market Request and `wait_for_devices == true`) 69 | * `delete` - (Defaults to 60 mins) Used when destroying the Spot Market Request and `wait for devices == true` 70 | 71 | ## Attributes Reference 72 | 73 | The following attributes are exported: 74 | 75 | * `id` - The ID of the Spot Market Request 76 | * `facilities` - The facilities where the Spot Market Request is applied. This is computed when `metro` is set or no specific location was requested. 77 | 78 | ## Import 79 | 80 | This resource can be imported using an existing spot market request ID: 81 | 82 | ```sh 83 | terraform import metal_spot_market_request {existing_spot_market_request_id} 84 | ``` 85 | -------------------------------------------------------------------------------- /metal/datasource_metal_project_ssh_key.go: -------------------------------------------------------------------------------- 1 | package metal 2 | 3 | import ( 4 | "fmt" 5 | "path" 6 | "strings" 7 | 8 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" 9 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" 10 | "github.com/packethost/packngo" 11 | ) 12 | 13 | func dataSourceMetalProjectSSHKey() *schema.Resource { 14 | return &schema.Resource{ 15 | DeprecationMessage: deprecatedProviderMsg, 16 | Read: dataSourceMetalProjectSSHKeyRead, 17 | Schema: map[string]*schema.Schema{ 18 | "search": { 19 | Type: schema.TypeString, 20 | Description: "The name, fingerprint, id, or public_key of the SSH Key to search for in the Equinix Metal project", 21 | Optional: true, 22 | ValidateFunc: validation.NoZeroValues, 23 | }, 24 | "id": { 25 | Type: schema.TypeString, 26 | Description: "The id of the SSH Key", 27 | Optional: true, 28 | ValidateFunc: validation.NoZeroValues, 29 | Computed: true, 30 | }, 31 | "project_id": { 32 | Type: schema.TypeString, 33 | Description: "The Equinix Metal project id of the Equinix Metal SSH Key", 34 | Required: true, 35 | ValidateFunc: validation.NoZeroValues, 36 | }, 37 | "name": { 38 | Type: schema.TypeString, 39 | Description: "The label of the Equinix Metal SSH Key", 40 | Computed: true, 41 | }, 42 | "public_key": { 43 | Type: schema.TypeString, 44 | Description: "The public SSH key that will be authorized for SSH access on Equinix Metal devices provisioned with this key", 45 | Computed: true, 46 | }, 47 | "fingerprint": { 48 | Type: schema.TypeString, 49 | Computed: true, 50 | }, 51 | "created": { 52 | Type: schema.TypeString, 53 | Computed: true, 54 | }, 55 | "updated": { 56 | Type: schema.TypeString, 57 | Computed: true, 58 | }, 59 | "owner_id": { 60 | Type: schema.TypeString, 61 | Computed: true, 62 | }, 63 | }, 64 | } 65 | } 66 | 67 | func dataSourceMetalProjectSSHKeyRead(d *schema.ResourceData, meta interface{}) error { 68 | client := meta.(*packngo.Client) 69 | 70 | search := d.Get("search").(string) 71 | id := d.Get("id").(string) 72 | projectID := d.Get("project_id").(string) 73 | 74 | if id == "" && search == "" { 75 | return fmt.Errorf("You must supply either search or id") 76 | } 77 | 78 | var ( 79 | key packngo.SSHKey 80 | searchOpts *packngo.SearchOptions 81 | ) 82 | 83 | if search != "" { 84 | searchOpts = &packngo.SearchOptions{Search: search} 85 | } 86 | keys, _, err := client.Projects.ListSSHKeys(projectID, searchOpts) 87 | 88 | if err != nil { 89 | err = fmt.Errorf("Error listing project ssh keys: %s", friendlyError(err)) 90 | return err 91 | } 92 | 93 | for i := range keys { 94 | // use the first match for searches 95 | if search != "" { 96 | key = keys[i] 97 | break 98 | } 99 | 100 | // otherwise find the matching ID 101 | if keys[i].ID == id { 102 | key = keys[i] 103 | break 104 | } 105 | } 106 | 107 | if key.ID == "" { 108 | // Not Found 109 | return fmt.Errorf("Project %q SSH Key matching %q was not found", projectID, search) 110 | } 111 | 112 | ownerID := path.Base(key.Owner.Href) 113 | 114 | d.SetId(key.ID) 115 | d.Set("name", key.Label) 116 | d.Set("public_key", key.Key) 117 | d.Set("fingerprint", key.FingerPrint) 118 | d.Set("owner_id", ownerID) 119 | d.Set("created", key.Created) 120 | d.Set("updated", key.Updated) 121 | 122 | if strings.Contains(key.Owner.Href, "/projects/") { 123 | d.Set("project_id", ownerID) 124 | } 125 | 126 | return nil 127 | } 128 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Hello Contributors 2 | 3 | Thx for your interest! We're so glad you're here. 4 | 5 | Before continuing, please note that this project is deprecated and all new work should be started in the [Equinix Terraform Provider](https://github.com/equinix/terraform-provider-equinix). See the [README.md](README.md) for more details. 6 | 7 | ## Contributing 8 | ### Important Resources 9 | 10 | - bugs: [https://github.com/equinix/terraform-provider-metal/issues](https://github.com/equinix/terraform-provider-metal/issues) 11 | - features: [https://github.com/equinix/terraform-provider-metal/issues](https://github.com/equinix/terraform-provider-metal/issues) 12 | 13 | ### Code of Conduct 14 | 15 | Available via [https://github.com/equinix/terraform-provider-metal/blob/main/.github/CODE_OF_CONDUCT.md](https://github.com/equinix/terraform-provider-metal/blob/main/.github/CODE_OF_CONDUCT.md) 16 | 17 | ### Environment Details 18 | 19 | [https://github.com/equinix/terraform-provider-metal/blob/main/GNUmakefile](https://github.com/equinix/terraform-provider-metal/blob/main/GNUmakefile) 20 | 21 | ### How to Submit Change Requests 22 | 23 | Please submit change requests and / or features via [Issues](https://github.com/equinix/terraform-provider-metal/issues). There's no guarantee it'll be changed, but you never know until you try. We'll try to add comments as soon as possible, though. 24 | 25 | ### How to Report a Bug 26 | 27 | Bugs are problems in code, in the functionality of an application or in its UI design; you can submit them through [Issues/(https://github.com/equinix/terraform-provider-metal/issues) as well. 28 | 29 | ## Development 30 | ### Requirements 31 | 32 | - [Terraform 0.12+](https://www.terraform.io/downloads.html) (for v1.0.0 of this provider and newer) 33 | - [Go](https://golang.org/doc/install) 1.13 (to build the provider plugin) 34 | 35 | ### Building the provider 36 | 37 | Clone the repository, enter the provider directory, and build the provider. 38 | 39 | ```sh 40 | git clone git@github.com:equinix/terraform-provider-metal 41 | cd terraform-provider-metal 42 | make build 43 | ``` 44 | 45 | ### Developing the provider 46 | 47 | If you wish to work on the provider, you'll first need [Go](http://www.golang.org) installed on your machine (version 1.13+ is *required*). You'll also need to correctly setup a [GOPATH](http://golang.org/doc/code.html#GOPATH), as well as adding `$GOPATH/bin` to your `$PATH`. 48 | 49 | To compile the provider, run `make build`. This will build the provider and put the provider binary in the `$GOPATH/bin` directory. 50 | 51 | ```sh 52 | $ make bin 53 | ... 54 | $ $GOPATH/bin/terraform-provider-metal 55 | ... 56 | ``` 57 | 58 | ### Testing provider code 59 | 60 | We have mostly acceptance tests in the provider. There's no point for you to run them all, but you should run the one covering the functionality which you change. The acceptance test run will cost you some money, so feel free to abstain. The acceptance test suite will be run for your PR during the review process. 61 | 62 | To run an acceptance test, find the relevant test function in `*_test.go` (for example TestAccMetalDevice_Basic), and run it as 63 | 64 | ```sh 65 | TF_ACC=1 go test -v -timeout=20m ./... -run=TestAccMetalDevice_Basic 66 | ``` 67 | 68 | If you want to see HTTP traffic, set `TF_LOG=DEBUG`, i.e. 69 | 70 | ```sh 71 | TF_LOG=DEBUG TF_ACC=1 go test -v -timeout=20m ./... -run=TestAccMetalDevice_Basic 72 | ``` 73 | 74 | ### Testing the provider with Terraform 75 | 76 | Once you've built the plugin binary (see [Developing the provider](#developing-the-provider) above), it can be incorporated within your Terraform environment using the `-plugin-dir` option. Subsequent runs of Terraform will then use the plugin from your development environment. 77 | 78 | ```sh 79 | terraform init -plugin-dir $GOPATH/bin 80 | ``` 81 | -------------------------------------------------------------------------------- /metal/datasource_metal_spot_market_request.go: -------------------------------------------------------------------------------- 1 | package metal 2 | 3 | import ( 4 | "strings" 5 | "time" 6 | 7 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" 8 | "github.com/packethost/packngo" 9 | ) 10 | 11 | func dataSourceMetalSpotMarketRequest() *schema.Resource { 12 | return &schema.Resource{ 13 | DeprecationMessage: deprecatedProviderMsg, 14 | Read: dataSourceMetalSpotMarketRequestRead, 15 | Schema: map[string]*schema.Schema{ 16 | "request_id": { 17 | Type: schema.TypeString, 18 | Description: "The id of the Spot Market Request", 19 | Required: true, 20 | }, 21 | "device_ids": { 22 | Type: schema.TypeList, 23 | Description: "List of IDs of devices spawned by the referenced Spot Market Request", 24 | Computed: true, 25 | Elem: &schema.Schema{Type: schema.TypeString}, 26 | }, 27 | "devices_min": { 28 | Type: schema.TypeInt, 29 | Description: "Miniumum number devices to be created", 30 | Computed: true, 31 | }, 32 | "devices_max": { 33 | Type: schema.TypeInt, 34 | Description: "Maximum number devices to be created", 35 | Computed: true, 36 | }, 37 | "max_bid_price": { 38 | Type: schema.TypeFloat, 39 | Description: "Maximum price user is willing to pay per hour per device", 40 | Computed: true, 41 | }, 42 | "facilities": { 43 | Type: schema.TypeList, 44 | Elem: &schema.Schema{Type: schema.TypeString}, 45 | Description: "Facility IDs where devices should be created", 46 | Computed: true, 47 | }, 48 | "metro": { 49 | Type: schema.TypeString, 50 | Description: "Metro where devices should be created.", 51 | Computed: true, 52 | }, 53 | "project_id": { 54 | Type: schema.TypeString, 55 | Description: "Project ID", 56 | Computed: true, 57 | }, 58 | "plan": { 59 | Type: schema.TypeString, 60 | Description: "The device plan slug.", 61 | Computed: true, 62 | }, 63 | "end_at": { 64 | Type: schema.TypeString, 65 | Description: "Date and time When the spot market request will be ended.", 66 | Computed: true, 67 | }, 68 | }, 69 | Timeouts: resourceDefaultTimeouts, 70 | } 71 | } 72 | func dataSourceMetalSpotMarketRequestRead(d *schema.ResourceData, meta interface{}) error { 73 | client := meta.(*packngo.Client) 74 | id := d.Get("request_id").(string) 75 | 76 | smr, _, err := client.SpotMarketRequests.Get(id, &packngo.GetOptions{Includes: []string{"project", "devices", "facilities", "metro"}}) 77 | if err != nil { 78 | err = friendlyError(err) 79 | if isNotFound(err) { 80 | d.SetId("") 81 | return nil 82 | } 83 | return err 84 | } 85 | 86 | deviceIDs := make([]string, len(smr.Devices)) 87 | for i, d := range smr.Devices { 88 | deviceIDs[i] = d.ID 89 | 90 | } 91 | 92 | facs := smr.Facilities 93 | facCodes := []string{} 94 | 95 | for _, f := range facs { 96 | facCodes = append(facCodes, f.Code) 97 | } 98 | 99 | d.SetId(id) 100 | 101 | return setMap(d, map[string]interface{}{ 102 | "device_ids": deviceIDs, 103 | "end_at": func(d *schema.ResourceData, k string) error { 104 | if smr.EndAt != nil { 105 | return d.Set(k, smr.EndAt.Format(time.RFC3339)) 106 | } 107 | return nil 108 | }, 109 | "devices_max": smr.DevicesMax, 110 | "devices_min": smr.DevicesMin, 111 | "facilities": facCodes, 112 | "metro": func(d *schema.ResourceData, k string) error { 113 | if smr.Metro != nil { 114 | return d.Set(k, strings.ToLower(smr.Metro.Code)) 115 | } 116 | return nil 117 | }, 118 | "max_bid_price": smr.MaxBidPrice, 119 | "plan": smr.Plan.Slug, 120 | "project_id": smr.Project.ID, 121 | // TODO: created_at is not in packngo 122 | }) 123 | } 124 | -------------------------------------------------------------------------------- /metal/datasource_metal_hardware_reservation.go: -------------------------------------------------------------------------------- 1 | package metal 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" 7 | "github.com/packethost/packngo" 8 | ) 9 | 10 | func dataSourceMetalHardwareReservation() *schema.Resource { 11 | return &schema.Resource{ 12 | DeprecationMessage: deprecatedProviderMsg, 13 | Read: dataSourceMetalHardwareReservationRead, 14 | Schema: map[string]*schema.Schema{ 15 | "id": { 16 | Type: schema.TypeString, 17 | Optional: true, 18 | Computed: true, 19 | Description: "ID of the hardware reservation to look up", 20 | }, 21 | "device_id": { 22 | Type: schema.TypeString, 23 | Optional: true, 24 | Computed: true, 25 | Description: "UUID of device occupying the reservation", 26 | }, 27 | "short_id": { 28 | Type: schema.TypeString, 29 | Computed: true, 30 | Description: "Reservation short ID", 31 | }, 32 | "project_id": { 33 | Type: schema.TypeString, 34 | Computed: true, 35 | Description: "UUID of project this reservation is scoped to", 36 | }, 37 | "plan": { 38 | Type: schema.TypeString, 39 | Computed: true, 40 | Description: "Plan type for the reservation", 41 | }, 42 | "facility": { 43 | Type: schema.TypeString, 44 | Computed: true, 45 | Description: "Plan type for the reservation", 46 | }, 47 | "provisionable": { 48 | Type: schema.TypeBool, 49 | Computed: true, 50 | Description: "Flag indicating whether the reserved server is provisionable or not. Spare devices can't be provisioned unless they are activated first", 51 | }, 52 | "spare": { 53 | Type: schema.TypeBool, 54 | Computed: true, 55 | Description: "Flag indicating whether the Hardware Reservation is a spare. Spare Hardware Reservations are used when a Hardware Reservations requires service from Metal Equinix", 56 | }, 57 | "switch_uuid": { 58 | Type: schema.TypeString, 59 | Computed: true, 60 | Description: "Switch short ID, can be used to determine if two devices are connected to the same switch", 61 | }, 62 | }, 63 | } 64 | } 65 | 66 | func dataSourceMetalHardwareReservationRead(d *schema.ResourceData, meta interface{}) error { 67 | client := meta.(*packngo.Client) 68 | hrIdRaw, hrIdOk := d.GetOk("id") 69 | dIdRaw, dIdOk := d.GetOk("device_id") 70 | 71 | if dIdOk == hrIdOk { 72 | return fmt.Errorf("You must set one of id and device_id") 73 | } 74 | 75 | var deviceId string 76 | var hr *packngo.HardwareReservation 77 | 78 | if dIdOk { 79 | deviceId = dIdRaw.(string) 80 | includes := []string{ 81 | "hardware_reservation.project", 82 | "hardware_reservation.facility", 83 | } 84 | d, _, err := client.Devices.Get(deviceId, &packngo.GetOptions{Includes: includes}) 85 | if err != nil { 86 | return err 87 | } 88 | if d.HardwareReservation == nil { 89 | return fmt.Errorf("Device %s is not in a hardware reservation", deviceId) 90 | } 91 | hr = d.HardwareReservation 92 | 93 | } else { 94 | var err error 95 | hr, _, err = client.HardwareReservations.Get( 96 | hrIdRaw.(string), 97 | &packngo.GetOptions{Includes: []string{"project", "facility", "device"}}) 98 | if err != nil { 99 | return err 100 | } 101 | if hr.Device != nil { 102 | deviceId = hr.Device.ID 103 | } 104 | } 105 | 106 | m := map[string]interface{}{ 107 | "short_id": hr.ShortID, 108 | "project_id": hr.Project.ID, 109 | "device_id": deviceId, 110 | "plan": hr.Plan.Slug, 111 | "facility": hr.Facility.Code, 112 | "provisionable": hr.Provisionable, 113 | "spare": hr.Spare, 114 | "switch_uuid": hr.SwitchUUID, 115 | } 116 | 117 | d.SetId(hr.ID) 118 | return setMap(d, m) 119 | } 120 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/equinix/terraform-provider-metal 2 | 3 | require ( 4 | github.com/hashicorp/errwrap v1.0.0 5 | github.com/hashicorp/go-multierror v1.0.0 6 | github.com/hashicorp/go-retryablehttp v0.6.6 7 | github.com/hashicorp/terraform-plugin-sdk/v2 v2.9.0 8 | github.com/packethost/packngo v0.25.0 9 | github.com/stretchr/testify v1.7.0 10 | ) 11 | 12 | require ( 13 | cloud.google.com/go v0.61.0 // indirect 14 | cloud.google.com/go/storage v1.10.0 // indirect 15 | github.com/agext/levenshtein v1.2.2 // indirect 16 | github.com/apparentlymart/go-cidr v1.0.1 // indirect 17 | github.com/apparentlymart/go-textseg v1.0.0 // indirect 18 | github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect 19 | github.com/aws/aws-sdk-go v1.25.3 // indirect 20 | github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect 21 | github.com/davecgh/go-spew v1.1.1 // indirect 22 | github.com/fatih/color v1.7.0 // indirect 23 | github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect 24 | github.com/golang/protobuf v1.4.2 // indirect 25 | github.com/google/go-cmp v0.5.6 // indirect 26 | github.com/googleapis/gax-go/v2 v2.0.5 // indirect 27 | github.com/hashicorp/go-checkpoint v0.5.0 // indirect 28 | github.com/hashicorp/go-cleanhttp v0.5.2 // indirect 29 | github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320 // indirect 30 | github.com/hashicorp/go-getter v1.5.3 // indirect 31 | github.com/hashicorp/go-hclog v0.15.0 // indirect 32 | github.com/hashicorp/go-plugin v1.4.1 // indirect 33 | github.com/hashicorp/go-safetemp v1.0.0 // indirect 34 | github.com/hashicorp/go-uuid v1.0.1 // indirect 35 | github.com/hashicorp/go-version v1.3.0 // indirect 36 | github.com/hashicorp/hcl/v2 v2.3.0 // indirect 37 | github.com/hashicorp/logutils v1.0.0 // indirect 38 | github.com/hashicorp/terraform-exec v0.15.0 // indirect 39 | github.com/hashicorp/terraform-json v0.13.0 // indirect 40 | github.com/hashicorp/terraform-plugin-go v0.4.0 // indirect 41 | github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d // indirect 42 | github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af // indirect 43 | github.com/jstemmer/go-junit-report v0.9.1 // indirect 44 | github.com/klauspost/compress v1.11.2 // indirect 45 | github.com/mattn/go-colorable v0.1.4 // indirect 46 | github.com/mattn/go-isatty v0.0.10 // indirect 47 | github.com/mitchellh/copystructure v1.2.0 // indirect 48 | github.com/mitchellh/go-homedir v1.1.0 // indirect 49 | github.com/mitchellh/go-testing-interface v1.0.4 // indirect 50 | github.com/mitchellh/go-wordwrap v1.0.0 // indirect 51 | github.com/mitchellh/mapstructure v1.1.2 // indirect 52 | github.com/mitchellh/reflectwalk v1.0.2 // indirect 53 | github.com/oklog/run v1.0.0 // indirect 54 | github.com/pmezard/go-difflib v1.0.0 // indirect 55 | github.com/ulikunitz/xz v0.5.8 // indirect 56 | github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect 57 | github.com/zclconf/go-cty v1.9.1 // indirect 58 | go.opencensus.io v0.22.4 // indirect 59 | golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b // indirect 60 | golang.org/x/lint v0.0.0-20200302205851-738671d3881b // indirect 61 | golang.org/x/mod v0.3.0 // indirect 62 | golang.org/x/net v0.0.0-20210326060303-6b1517762897 // indirect 63 | golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d // indirect 64 | golang.org/x/sys v0.0.0-20210502180810-71e4cd670f79 // indirect 65 | golang.org/x/text v0.3.5 // indirect 66 | golang.org/x/tools v0.0.0-20200713011307-fd294ab11aed // indirect 67 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 // indirect 68 | google.golang.org/api v0.29.0 // indirect 69 | google.golang.org/appengine v1.6.6 // indirect 70 | google.golang.org/genproto v0.0.0-20200711021454-869866162049 // indirect 71 | google.golang.org/grpc v1.32.0 // indirect 72 | google.golang.org/protobuf v1.25.0 // indirect 73 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect 74 | ) 75 | 76 | go 1.17 77 | -------------------------------------------------------------------------------- /metal/resource_metal_gateway_test.go: -------------------------------------------------------------------------------- 1 | package metal 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" 8 | "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" 9 | "github.com/packethost/packngo" 10 | ) 11 | 12 | func testAccMetalGatewayConfig_PrivateIPv4() string { 13 | return fmt.Sprintf(` 14 | resource "metal_project" "test" { 15 | name = "%[1]s-pro-gateway-test" 16 | } 17 | 18 | resource "metal_vlan" "test" { 19 | description = "%[1]s-vlan in SV" 20 | metro = "sv" 21 | project_id = metal_project.test.id 22 | } 23 | 24 | resource "metal_gateway" "test" { 25 | project_id = metal_project.test.id 26 | vlan_id = metal_vlan.test.id 27 | private_ipv4_subnet_size = 8 28 | } 29 | `, tstResourcePrefix) 30 | } 31 | 32 | func TestAccMetalGateway_PrivateIPv4(t *testing.T) { 33 | resource.ParallelTest(t, resource.TestCase{ 34 | PreCheck: func() { testAccPreCheck(t) }, 35 | Providers: testAccProviders, 36 | CheckDestroy: testAccCheckMetalGatewayDestroyed, 37 | Steps: []resource.TestStep{ 38 | { 39 | Config: testAccMetalGatewayConfig_PrivateIPv4(), 40 | Check: resource.ComposeTestCheckFunc( 41 | resource.TestCheckResourceAttrPair( 42 | "metal_gateway.test", "project_id", 43 | "metal_project.test", "id"), 44 | resource.TestCheckResourceAttr( 45 | "metal_gateway.test", "private_ipv4_subnet_size", "8"), 46 | ), 47 | }, 48 | }, 49 | }) 50 | } 51 | 52 | func testAccMetalGatewayConfig_ExistingReservation() string { 53 | return fmt.Sprintf(` 54 | resource "metal_project" "test" { 55 | name = "%[1]s-pro-gateway-test" 56 | } 57 | 58 | resource "metal_vlan" "test" { 59 | description = "%[1]s-vlan in SV" 60 | metro = "sv" 61 | project_id = metal_project.test.id 62 | } 63 | 64 | resource "metal_reserved_ip_block" "test" { 65 | project_id = metal_project.test.id 66 | metro = "sv" 67 | quantity = 8 68 | } 69 | 70 | resource "metal_gateway" "test" { 71 | project_id = metal_project.test.id 72 | vlan_id = metal_vlan.test.id 73 | ip_reservation_id = metal_reserved_ip_block.test.id 74 | } 75 | `, tstResourcePrefix) 76 | } 77 | 78 | func TestAccMetalGateway_ExistingReservation(t *testing.T) { 79 | resource.ParallelTest(t, resource.TestCase{ 80 | PreCheck: func() { testAccPreCheck(t) }, 81 | Providers: testAccProviders, 82 | CheckDestroy: testAccCheckMetalGatewayDestroyed, 83 | Steps: []resource.TestStep{ 84 | { 85 | Config: testAccMetalGatewayConfig_ExistingReservation(), 86 | Check: resource.ComposeTestCheckFunc( 87 | resource.TestCheckResourceAttrPair( 88 | "metal_gateway.test", "project_id", 89 | "metal_project.test", "id"), 90 | resource.TestCheckResourceAttrPair( 91 | "metal_gateway.test", "ip_reservation_id", 92 | "metal_reserved_ip_block.test", "id"), 93 | ), 94 | }, 95 | }, 96 | }) 97 | } 98 | 99 | func testAccCheckMetalGatewayDestroyed(s *terraform.State) error { 100 | client := testAccProvider.Meta().(*packngo.Client) 101 | 102 | for _, rs := range s.RootModule().Resources { 103 | if rs.Type != "metal_gateway" { 104 | continue 105 | } 106 | if _, _, err := client.MetalGateways.Get(rs.Primary.ID, nil); err == nil { 107 | return fmt.Errorf("Gateway still exists") 108 | } 109 | } 110 | 111 | return nil 112 | } 113 | 114 | func TestAccMetalGateway_importBasic(t *testing.T) { 115 | resource.ParallelTest(t, resource.TestCase{ 116 | PreCheck: func() { testAccPreCheck(t) }, 117 | Providers: testAccProviders, 118 | CheckDestroy: testAccCheckMetalGatewayDestroyed, 119 | Steps: []resource.TestStep{ 120 | { 121 | Config: testAccMetalGatewayConfig_PrivateIPv4(), 122 | }, 123 | { 124 | ResourceName: "metal_gateway.test", 125 | ImportState: true, 126 | ImportStateVerify: true, 127 | }, 128 | }, 129 | }) 130 | } 131 | -------------------------------------------------------------------------------- /metal/resource_metal_device_network_type.go: -------------------------------------------------------------------------------- 1 | package metal 2 | 3 | import ( 4 | "log" 5 | 6 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" 7 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" 8 | "github.com/packethost/packngo" 9 | ) 10 | 11 | func resourceMetalDeviceNetworkType() *schema.Resource { 12 | return &schema.Resource{ 13 | DeprecationMessage: deprecatedProviderMsg, 14 | Create: resourceMetalDeviceNetworkTypeCreate, 15 | Read: resourceMetalDeviceNetworkTypeRead, 16 | Delete: resourceMetalDeviceNetworkTypeDelete, 17 | Update: resourceMetalDeviceNetworkTypeUpdate, 18 | Importer: &schema.ResourceImporter{ 19 | State: schema.ImportStatePassthrough, 20 | }, 21 | 22 | Schema: map[string]*schema.Schema{ 23 | "device_id": { 24 | Type: schema.TypeString, 25 | Description: "The ID of the device on which the network type should be set", 26 | Required: true, 27 | ForceNew: true, 28 | }, 29 | "type": { 30 | Type: schema.TypeString, 31 | Description: "Network type to set. Must be one of " + NetworkTypeListHB, 32 | Required: true, 33 | ValidateFunc: validation.StringInSlice(DeviceNetworkTypesHB, false), 34 | }, 35 | }, 36 | } 37 | } 38 | 39 | func getDevIDandNetworkType(d *schema.ResourceData, c *packngo.Client) (string, string, error) { 40 | deviceID := d.Id() 41 | if len(deviceID) == 0 { 42 | deviceID = d.Get("device_id").(string) 43 | } 44 | 45 | dev, _, err := c.Devices.Get(deviceID, nil) 46 | if err != nil { 47 | return "", "", err 48 | } 49 | devType := dev.GetNetworkType() 50 | 51 | return dev.ID, devType, nil 52 | } 53 | 54 | func getAndPossiblySetNetworkType(d *schema.ResourceData, c *packngo.Client, targetType string) error { 55 | // "hybrid-bonded" is an alias for "layer3" with VLAN(s) connected. We use 56 | // other resource for VLAN attachment, so we treat these two as equivalent 57 | if targetType == "hybrid-bonded" { 58 | targetType = "layer3" 59 | } 60 | devID, devType, err := getDevIDandNetworkType(d, c) 61 | if err != nil { 62 | return err 63 | } 64 | 65 | if devType != targetType { 66 | _, err := c.DevicePorts.DeviceToNetworkType(devID, targetType) 67 | if err != nil { 68 | return err 69 | } 70 | } 71 | return nil 72 | } 73 | 74 | func resourceMetalDeviceNetworkTypeCreate(d *schema.ResourceData, meta interface{}) error { 75 | client := meta.(*packngo.Client) 76 | ntype := d.Get("type").(string) 77 | 78 | err := getAndPossiblySetNetworkType(d, client, ntype) 79 | if err != nil { 80 | return err 81 | } 82 | d.SetId(d.Get("device_id").(string)) 83 | return resourceMetalDeviceNetworkTypeRead(d, meta) 84 | } 85 | 86 | func resourceMetalDeviceNetworkTypeRead(d *schema.ResourceData, meta interface{}) error { 87 | client := meta.(*packngo.Client) 88 | 89 | _, devNType, err := getDevIDandNetworkType(d, client) 90 | 91 | if err != nil { 92 | err = friendlyError(err) 93 | 94 | if isNotFound(err) { 95 | log.Printf("[WARN] Device (%s) for Network Type request not found, removing from state", d.Id()) 96 | d.SetId("") 97 | return nil 98 | } 99 | 100 | return err 101 | } 102 | 103 | // if "hybrid-bonded" is set as desired state and current state is "layer3", 104 | // keep the value in "hybrid-bonded" 105 | currentType := d.Get("type").(string) 106 | if currentType == "hybrid-bonded" && devNType == "layer3" { 107 | devNType = "hybrid-bonded" 108 | } 109 | 110 | d.Set("type", devNType) 111 | 112 | return nil 113 | } 114 | 115 | func resourceMetalDeviceNetworkTypeUpdate(d *schema.ResourceData, meta interface{}) error { 116 | client := meta.(*packngo.Client) 117 | ntype := d.Get("type").(string) 118 | if d.HasChange("type") { 119 | err := getAndPossiblySetNetworkType(d, client, ntype) 120 | if err != nil { 121 | return err 122 | } 123 | } 124 | 125 | return resourceMetalDeviceNetworkTypeRead(d, meta) 126 | } 127 | 128 | func resourceMetalDeviceNetworkTypeDelete(d *schema.ResourceData, meta interface{}) error { 129 | return nil 130 | } 131 | -------------------------------------------------------------------------------- /docs/resources/vrf.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_title: "Equinix Metal: metal_vrf" 3 | subcategory: "" 4 | description: |- 5 | (not-GA) Provides a resource for Equinix Metal VRF. 6 | --- 7 | 8 | # metal_vrf (Resource) 9 | 10 | !> **PROVIDER DEPRECATED:** Equinix Metal Provider is now Deprecated. Please consider using [`equinix_metal_vrf`](https://registry.terraform.io/providers/equinix/equinix/latest/docs/resources/equinix_metal_vrf) resource from the [Equinix provider](https://registry.terraform.io/providers/equinix/equinix/latest/docs) instead of `metal_vrf`. [See the Metal provider section for more details](../index.md#equinix-metal-provider) on the new provider and available migration guides. 11 | 12 | Use this resource to manage a VRF. 13 | 14 | ~> VRF features are not generally available. The interfaces related to VRF resources may change ahead of general availability. 15 | 16 | ## Example Usage 17 | 18 | Create a VRF in your desired metro and project with any IP ranges that you want the VRF to route and forward. 19 | 20 | ```hcl 21 | resource "metal_project" "example" { 22 | name = "example" 23 | } 24 | 25 | resource "metal_vrf" "example" { 26 | description = "VRF with ASN 65000 and a pool of address space that includes 192.168.100.0/25" 27 | name = "example-vrf" 28 | metro = "da" 29 | local_asn = "65000" 30 | ip_ranges = ["192.168.100.0/25", "192.168.200.0/25"] 31 | project_id = metal_project.example.id 32 | } 33 | ``` 34 | 35 | Create IP reservations and assign them to a Metal Gateway resources. The Gateway will be assigned the first address in the block. 36 | 37 | ```hcl 38 | resource "metal_reserved_ip_block" "example" { 39 | description = "Reserved IP block (192.168.100.0/29) taken from on of the ranges in the VRF's pool of address space." 40 | project_id = metal_project.example.id 41 | metro = metal_vrf.example.metro 42 | type = "vrf" 43 | vrf_id = metal_vrf.example.id 44 | cidr = 29 45 | network = "192.168.100.0" 46 | } 47 | 48 | resource "metal_vlan" "example" { 49 | description = "A VLAN for Layer2 and Hybrid Metal devices" 50 | metro = metal_vrf.example.metro 51 | project_id = metal_project.example.id 52 | } 53 | 54 | resource "metal_gateway" "example" { 55 | description = "A Gateway on the VRF192.168.100.0/29 56 | project_id = metal_project.example.id 57 | vlan_id = metal_vlan.example.id 58 | ip_reservation_id = metal_reserved_ip_block.example.id 59 | } 60 | ``` 61 | 62 | Attach a Virtual Circuit from a Dedicated Metal Connection to the Metal Gateway. 63 | 64 | ```hcl 65 | data "metal_connection" "example" { 66 | connection_id = var.metal_dedicated_connection_id 67 | } 68 | 69 | resource "metal_virtual_circuit" "example" { 70 | name = "example-vc" 71 | description = "Virtual Circuit" 72 | connection_id = data.metal_connection.example.id 73 | project_id = metal_project.example.id 74 | port_id = data.metal_connection.example.ports[0].id 75 | nni_vlan = 1024 76 | vrf_id = metal_vrf.example.id 77 | peer_asn = 65530 78 | subnet = "192.168.100.16/31" 79 | metal_ip = "192.168.100.16" 80 | customer_ip = "192.168.100.17" 81 | } 82 | ``` 83 | 84 | ## Argument Reference 85 | 86 | The following arguments are supported: 87 | 88 | * `name` - (Required) User-supplied name of the VRF, unique to the project 89 | * `metro` - (Required) Metro ID or Code where the VRF will be deployed. 90 | * `project_id` - (Required) Project ID where the VRF will be deployed. 91 | * `description` - (Optional) Description of the VRF. 92 | * `local_asn` - (Optional) The 4-byte ASN set on the VRF. 93 | * `ip_ranges` - (Optional) All IPv4 and IPv6 Ranges that will be available to BGP Peers. IPv4 addresses must be /8 or smaller with a minimum size of /29. IPv6 must be /56 or smaller with a minimum size of /64. Ranges must not overlap other ranges within the VRF. 94 | 95 | ## Attributes Reference 96 | 97 | No additional attributes are exported. 98 | 99 | ## Import 100 | 101 | This resource can be imported using an existing VRF ID: 102 | 103 | ```sh 104 | terraform import metal_vrf {existing_id} 105 | ``` 106 | -------------------------------------------------------------------------------- /docs/data-sources/device.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_title: "Equinix Metal: metal_device" 3 | subcategory: "" 4 | description: |- 5 | Provides an Equinix Metal device datasource. This can be used to read existing devices. 6 | --- 7 | 8 | # metal_device (Data Source) 9 | 10 | !> **PROVIDER DEPRECATED:** Equinix Metal Provider is now Deprecated. Please consider using [`equinix_metal_device`](https://registry.terraform.io/providers/equinix/equinix/latest/docs/data-sources/equinix_metal_device) data source from the [Equinix provider](https://registry.terraform.io/providers/equinix/equinix/latest/docs) instead of `metal_device`. [See the Metal provider section for more details](../index.md#equinix-metal-provider) on the new provider and available migration guides. 11 | 12 | Provides an Equinix Metal device datasource. 13 | 14 | ~> **Note:** All arguments including the `root_password` and `user_data` will be stored in 15 | the raw state as plain-text. 16 | [Read more about sensitive data in state](/docs/state/sensitive-data.html). 17 | 18 | ## Example Usage 19 | 20 | ```hcl 21 | # Fetch a device data by hostname and show it's ID 22 | 23 | data "metal_device" "test" { 24 | project_id = local.project_id 25 | hostname = "mydevice" 26 | } 27 | 28 | output "id" { 29 | value = data.metal_device.test.id 30 | } 31 | ``` 32 | 33 | ```hcl 34 | # Fetch a device data by ID and show its public IPv4 35 | data "metal_device" "test" {} 36 | 37 | output "ipv4" { 38 | value = data.metal_device.test.access_public_ipv4 39 | } 40 | ``` 41 | 42 | ## Argument Reference 43 | 44 | The following arguments are supported: 45 | 46 | * `hostname` - The device name 47 | * `project_id` - The id of the project in which the devices exists 48 | * `device_id` - Device ID 49 | 50 | User can lookup devices either by `device_id` or `project_id` and `hostname`. 51 | 52 | ## Attributes Reference 53 | 54 | The following attributes are exported: 55 | 56 | * `access_private_ipv4` - The ipv4 private IP assigned to the device 57 | * `access_public_ipv4` - The ipv4 management IP assigned to the device 58 | * `access_public_ipv6` - The ipv6 management IP assigned to the device 59 | * `billing_cycle` - The billing cycle of the device (monthly or hourly) 60 | * `facility` - The facility where the device is deployed. 61 | * `description` - Description string for the device 62 | * `hardware_reservation_id` - The id of hardware reservation which this device occupies 63 | * `id` - The ID of the device 64 | * `metro` - The metro where the device is deployed 65 | * `network` - The device's private and public IP (v4 and v6) network details. When a device is run without any special network configuration, it will have 3 networks: 66 | * Public IPv4 at `metal_device.name.network.0` 67 | * IPv6 at `metal_device.name.network.1` 68 | * Private IPv4 at `metal_device.name.network.2` 69 | Elastic addresses then stack by type - an assigned public IPv4 will go after the management public IPv4 (to index 1), and will then shift the indices of the IPv6 and private IPv4. Assigned private IPv4 will go after the management private IPv4 (to the end of the network list). 70 | The fields of the network attributes are: 71 | * `address` - IPv4 or IPv6 address string 72 | * `cidr` - Bit length of the network mask of the address 73 | * `gateway` - Address of router 74 | * `public` - Whether the address is routable from the Internet 75 | * `family` - IP version - "4" or "6" 76 | * `network_type` - L2 network type of the device, one of "layer3", "layer2-bonded", "layer2-individual", "hybrid" 77 | * `operating_system` - The operating system running on the device 78 | * `plan` - The hardware config of the device 79 | * `ports` - Ports assigned to the device 80 | * `name` - Name of the port (e.g. `eth0`, or `bond0`) 81 | * `id` - ID of the port 82 | * `type` - Type of the port (e.g. `NetworkPort` or `NetworkBondPort`) 83 | * `mac` - MAC address assigned to the port 84 | * `bonded` - Whether this port is part of a bond in bonded network setup 85 | * `root_password` - Root password to the server (if still available) 86 | * `ssh_key_ids` - List of IDs of SSH keys deployed in the device, can be both user or project SSH keys 87 | * `state` - The state of the device 88 | * `tags` - Tags attached to the device 89 | --------------------------------------------------------------------------------