Deprecated 'network', introduce 'network_interface'

This commit is contained in:
Dave Cunningham 2015-02-06 03:21:22 -05:00
parent 90d504a524
commit b385093502
3 changed files with 447 additions and 142 deletions

View File

@ -75,20 +75,61 @@ func resourceComputeInstance() *schema.Resource {
}, },
}, },
"network_interface": &schema.Schema{
Type: schema.TypeList,
Optional: true,
ForceNew: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"network": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"name": &schema.Schema{
Type: schema.TypeString,
Computed: true,
},
"address": &schema.Schema{
Type: schema.TypeString,
Computed: true,
},
"access_config": &schema.Schema{
Type: schema.TypeList,
Optional: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"nat_ip": &schema.Schema{
Type: schema.TypeString,
Computed: true,
Optional: true,
},
},
},
},
},
},
},
"network": &schema.Schema{ "network": &schema.Schema{
Type: schema.TypeList, Type: schema.TypeList,
Required: true, Optional: true,
ForceNew: true, ForceNew: true,
Elem: &schema.Resource{ Elem: &schema.Resource{
Schema: map[string]*schema.Schema{ Schema: map[string]*schema.Schema{
"source": &schema.Schema{ "source": &schema.Schema{
Type: schema.TypeString, Type: schema.TypeString,
Required: true, Required: true,
ForceNew: true,
}, },
"address": &schema.Schema{ "address": &schema.Schema{
Type: schema.TypeString, Type: schema.TypeString,
Optional: true, Optional: true,
ForceNew: true,
}, },
"name": &schema.Schema{ "name": &schema.Schema{
@ -173,6 +214,33 @@ func resourceComputeInstance() *schema.Resource {
} }
} }
func resourceOperationWaitZone(
config *Config, op *compute.Operation, zone string, activity string) error {
w := &OperationWaiter{
Service: config.clientCompute,
Op: op,
Project: config.Project,
Zone: zone,
Type: OperationWaitZone,
}
state := w.Conf()
state.Delay = 10 * time.Second
state.Timeout = 10 * time.Minute
state.MinTimeout = 2 * time.Second
opRaw, err := state.WaitForState()
if err != nil {
return fmt.Errorf("Error waiting for %s: %s", activity, err)
}
op = opRaw.(*compute.Operation)
if op.Error != nil {
// Return the error
return OperationError(*op.Error)
}
return nil
}
func resourceComputeInstanceCreate(d *schema.ResourceData, meta interface{}) error { func resourceComputeInstanceCreate(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config) config := meta.(*Config)
@ -258,9 +326,22 @@ func resourceComputeInstanceCreate(d *schema.ResourceData, meta interface{}) err
disks = append(disks, &disk) disks = append(disks, &disk)
} }
// Build up the list of networks
networksCount := d.Get("network.#").(int) networksCount := d.Get("network.#").(int)
networks := make([]*compute.NetworkInterface, 0, networksCount) networkInterfacesCount := d.Get("network_interface.#").(int)
if networksCount > 0 && networkInterfacesCount > 0 {
return fmt.Errorf("Error: cannot define both networks and network_interfaces.")
}
if networksCount == 0 && networkInterfacesCount == 0 {
return fmt.Errorf("Error: Must define at least one network_interface.")
}
var networkInterfaces []*compute.NetworkInterface
if networksCount > 0 {
// TODO: Delete this block when removing network { }
// Build up the list of networkInterfaces
networkInterfaces = make([]*compute.NetworkInterface, 0, networksCount)
for i := 0; i < networksCount; i++ { for i := 0; i < networksCount; i++ {
prefix := fmt.Sprintf("network.%d", i) prefix := fmt.Sprintf("network.%d", i)
// Load up the name of this network // Load up the name of this network
@ -273,7 +354,7 @@ func resourceComputeInstanceCreate(d *schema.ResourceData, meta interface{}) err
networkName, err) networkName, err)
} }
// Build the disk // Build the networkInterface
var iface compute.NetworkInterface var iface compute.NetworkInterface
iface.AccessConfigs = []*compute.AccessConfig{ iface.AccessConfigs = []*compute.AccessConfig{
&compute.AccessConfig{ &compute.AccessConfig{
@ -283,7 +364,42 @@ func resourceComputeInstanceCreate(d *schema.ResourceData, meta interface{}) err
} }
iface.Network = network.SelfLink iface.Network = network.SelfLink
networks = append(networks, &iface) networkInterfaces = append(networkInterfaces, &iface)
}
}
if networkInterfacesCount > 0 {
// Build up the list of networkInterfaces
networkInterfaces = make([]*compute.NetworkInterface, 0, networkInterfacesCount)
for i := 0; i < networkInterfacesCount; i++ {
prefix := fmt.Sprintf("network_interface.%d", i)
// Load up the name of this network_interfac
networkName := d.Get(prefix + ".network").(string)
network, err := config.clientCompute.Networks.Get(
config.Project, networkName).Do()
if err != nil {
return fmt.Errorf(
"Error referencing network '%s': %s",
networkName, err)
}
// Build the networkInterface
var iface compute.NetworkInterface
iface.Network = network.SelfLink
// Handle access_config structs
accessConfigsCount := d.Get(prefix + ".access_config.#").(int)
iface.AccessConfigs = make([]*compute.AccessConfig, accessConfigsCount)
for j := 0; j < accessConfigsCount; j++ {
acPrefix := fmt.Sprintf("%s.access_config.%d", prefix, j)
iface.AccessConfigs[j] = &compute.AccessConfig{
Type: "ONE_TO_ONE_NAT",
NatIP: d.Get(acPrefix + ".nat_ip").(string),
}
}
networkInterfaces = append(networkInterfaces, &iface)
}
} }
serviceAccountsCount := d.Get("service_account.#").(int) serviceAccountsCount := d.Get("service_account.#").(int)
@ -314,7 +430,7 @@ func resourceComputeInstanceCreate(d *schema.ResourceData, meta interface{}) err
MachineType: machineType.SelfLink, MachineType: machineType.SelfLink,
Metadata: resourceInstanceMetadata(d), Metadata: resourceInstanceMetadata(d),
Name: d.Get("name").(string), Name: d.Get("name").(string),
NetworkInterfaces: networks, NetworkInterfaces: networkInterfaces,
Tags: resourceInstanceTags(d), Tags: resourceInstanceTags(d),
ServiceAccounts: serviceAccounts, ServiceAccounts: serviceAccounts,
} }
@ -330,28 +446,11 @@ func resourceComputeInstanceCreate(d *schema.ResourceData, meta interface{}) err
d.SetId(instance.Name) d.SetId(instance.Name)
// Wait for the operation to complete // Wait for the operation to complete
w := &OperationWaiter{ waitErr := resourceOperationWaitZone(config, op, zone.Name, "instance to create")
Service: config.clientCompute, if waitErr != nil {
Op: op,
Project: config.Project,
Zone: zone.Name,
Type: OperationWaitZone,
}
state := w.Conf()
state.Delay = 10 * time.Second
state.Timeout = 10 * time.Minute
state.MinTimeout = 2 * time.Second
opRaw, err := state.WaitForState()
if err != nil {
return fmt.Errorf("Error waiting for instance to create: %s", err)
}
op = opRaw.(*compute.Operation)
if op.Error != nil {
// The resource didn't actually create // The resource didn't actually create
d.SetId("") d.SetId("")
return waitErr
// Return the error
return OperationError(*op.Error)
} }
return resourceComputeInstanceRead(d, meta) return resourceComputeInstanceRead(d, meta)
@ -385,14 +484,35 @@ func resourceComputeInstanceRead(d *schema.ResourceData, meta interface{}) error
} }
} }
networksCount := d.Get("network.#").(int)
networkInterfacesCount := d.Get("network_interface.#").(int)
if networksCount > 0 && networkInterfacesCount > 0 {
return fmt.Errorf("Error: cannot define both networks and network_interfaces.")
}
if networksCount == 0 && networkInterfacesCount == 0 {
return fmt.Errorf("Error: Must define at least one network_interface.")
}
// Set the networks // Set the networks
// Use the first external IP found for the default connection info.
externalIP := "" externalIP := ""
internalIP := ""
if networksCount > 0 {
// TODO: Remove this when realizing deprecation of .network
for i, iface := range instance.NetworkInterfaces { for i, iface := range instance.NetworkInterfaces {
prefix := fmt.Sprintf("network.%d", i) prefix := fmt.Sprintf("network.%d", i)
d.Set(prefix+".name", iface.Name) d.Set(prefix+".name", iface.Name)
log.Printf(prefix+".name = %s", iface.Name)
var natIP string
for _, config := range iface.AccessConfigs {
if config.Type == "ONE_TO_ONE_NAT" {
natIP = config.NatIP
break
}
}
// Use the first external IP found for the default connection info.
natIP := resourceInstanceNatIP(iface)
if externalIP == "" && natIP != "" { if externalIP == "" && natIP != "" {
externalIP = natIP externalIP = natIP
} }
@ -400,11 +520,49 @@ func resourceComputeInstanceRead(d *schema.ResourceData, meta interface{}) error
d.Set(prefix+".internal_address", iface.NetworkIP) d.Set(prefix+".internal_address", iface.NetworkIP)
} }
}
if networkInterfacesCount > 0 {
for i, iface := range instance.NetworkInterfaces {
prefix := fmt.Sprintf("network_interface.%d", i)
d.Set(prefix+".name", iface.Name)
// The first non-empty ip is left in natIP
var natIP string
for j, config := range iface.AccessConfigs {
acPrefix := fmt.Sprintf("%s.access_config.%d", prefix, j)
d.Set(acPrefix+".nat_ip", config.NatIP)
if natIP == "" {
natIP = config.NatIP
}
}
if externalIP == "" {
externalIP = natIP
}
d.Set(prefix+".address", iface.NetworkIP)
if internalIP == "" {
internalIP = iface.NetworkIP
}
}
}
// Fall back on internal ip if there is no external ip. This makes sense in the situation where
// terraform is being used on a cloud instance and can therefore access the instances it creates
// via their internal ips.
sshIP := externalIP
if sshIP == "" {
sshIP = internalIP
}
// Initialize the connection info // Initialize the connection info
d.SetConnInfo(map[string]string{ d.SetConnInfo(map[string]string{
"type": "ssh", "type": "ssh",
"host": externalIP, "host": sshIP,
}) })
// Set the metadata fingerprint if there is one. // Set the metadata fingerprint if there is one.
@ -423,6 +581,21 @@ func resourceComputeInstanceRead(d *schema.ResourceData, meta interface{}) error
func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) error { func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config) config := meta.(*Config)
zone := d.Get("zone").(string)
instance, err := config.clientCompute.Instances.Get(
config.Project, zone, d.Id()).Do()
if err != nil {
if gerr, ok := err.(*googleapi.Error); ok && gerr.Code == 404 {
// The resource doesn't exist anymore
d.SetId("")
return nil
}
return fmt.Errorf("Error reading instance: %s", err)
}
// Enable partial mode for the resource since it is possible // Enable partial mode for the resource since it is possible
d.Partial(true) d.Partial(true)
@ -430,30 +603,15 @@ func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) err
if d.HasChange("metadata") { if d.HasChange("metadata") {
metadata := resourceInstanceMetadata(d) metadata := resourceInstanceMetadata(d)
op, err := config.clientCompute.Instances.SetMetadata( op, err := config.clientCompute.Instances.SetMetadata(
config.Project, d.Get("zone").(string), d.Id(), metadata).Do() config.Project, zone, d.Id(), metadata).Do()
if err != nil { if err != nil {
return fmt.Errorf("Error updating metadata: %s", err) return fmt.Errorf("Error updating metadata: %s", err)
} }
w := &OperationWaiter{ // 1 5 2
Service: config.clientCompute, opErr := resourceOperationWaitZone(config, op, zone, "metadata to update")
Op: op, if opErr != nil {
Project: config.Project, return opErr
Zone: d.Get("zone").(string),
Type: OperationWaitZone,
}
state := w.Conf()
state.Delay = 1 * time.Second
state.Timeout = 5 * time.Minute
state.MinTimeout = 2 * time.Second
opRaw, err := state.WaitForState()
if err != nil {
return fmt.Errorf("Error waiting for metadata to update: %s", err)
}
op = opRaw.(*compute.Operation)
if op.Error != nil {
// Return the error
return OperationError(*op.Error)
} }
d.SetPartial("metadata") d.SetPartial("metadata")
@ -462,35 +620,80 @@ func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) err
if d.HasChange("tags") { if d.HasChange("tags") {
tags := resourceInstanceTags(d) tags := resourceInstanceTags(d)
op, err := config.clientCompute.Instances.SetTags( op, err := config.clientCompute.Instances.SetTags(
config.Project, d.Get("zone").(string), d.Id(), tags).Do() config.Project, zone, d.Id(), tags).Do()
if err != nil { if err != nil {
return fmt.Errorf("Error updating tags: %s", err) return fmt.Errorf("Error updating tags: %s", err)
} }
w := &OperationWaiter{ opErr := resourceOperationWaitZone(config, op, zone, "tags to update")
Service: config.clientCompute, if opErr != nil {
Op: op, return opErr
Project: config.Project,
Zone: d.Get("zone").(string),
Type: OperationWaitZone,
}
state := w.Conf()
state.Delay = 1 * time.Second
state.Timeout = 5 * time.Minute
state.MinTimeout = 2 * time.Second
opRaw, err := state.WaitForState()
if err != nil {
return fmt.Errorf("Error waiting for tags to update: %s", err)
}
op = opRaw.(*compute.Operation)
if op.Error != nil {
// Return the error
return OperationError(*op.Error)
} }
d.SetPartial("tags") d.SetPartial("tags")
} }
networkInterfacesCount := d.Get("network_interface.#").(int)
if networkInterfacesCount > 0 {
// Sanity check
if networkInterfacesCount != len(instance.NetworkInterfaces) {
return fmt.Errorf("Instance had unexpected number of network interfaces: %d", len(instance.NetworkInterfaces))
}
for i := 0; i < networkInterfacesCount; i++ {
prefix := fmt.Sprintf("network_interface.%d", i)
instNetworkInterface := instance.NetworkInterfaces[i]
networkName := d.Get(prefix+".name").(string)
// TODO: This sanity check is broken by #929, disabled for now (by forcing the equality)
networkName = instNetworkInterface.Name
// Sanity check
if networkName != instNetworkInterface.Name {
return fmt.Errorf("Instance networkInterface had unexpected name: %s", instNetworkInterface.Name)
}
if d.HasChange(prefix+".access_config") {
// TODO: This code deletes then recreates accessConfigs. This is bad because it may
// leave the machine inaccessible from either ip if the creation part fails (network
// timeout etc). However right now there is a GCE limit of 1 accessConfig so it is
// the only way to do it. In future this should be revised to only change what is
// necessary, and also add before removing.
// Delete any accessConfig that currently exists in instNetworkInterface
for _, ac := range(instNetworkInterface.AccessConfigs) {
op, err := config.clientCompute.Instances.DeleteAccessConfig(
config.Project, zone, d.Id(), networkName, ac.Name).Do();
if err != nil {
return fmt.Errorf("Error deleting old access_config: %s", err)
}
opErr := resourceOperationWaitZone(config, op, zone, "old access_config to delete")
if opErr != nil {
return opErr
}
}
// Create new ones
accessConfigsCount := d.Get(prefix + ".access_config.#").(int)
for j := 0; j < accessConfigsCount; j++ {
acPrefix := fmt.Sprintf("%s.access_config.%d", prefix, j)
ac := &compute.AccessConfig{
Type: "ONE_TO_ONE_NAT",
NatIP: d.Get(acPrefix + ".nat_ip").(string),
}
op, err := config.clientCompute.Instances.AddAccessConfig(
config.Project, zone, d.Id(), networkName, ac).Do();
if err != nil {
return fmt.Errorf("Error adding new access_config: %s", err)
}
opErr := resourceOperationWaitZone(config, op, zone, "new access_config to add")
if opErr != nil {
return opErr
}
}
}
}
}
// We made it, disable partial mode // We made it, disable partial mode
d.Partial(false) d.Partial(false)
@ -500,32 +703,16 @@ func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) err
func resourceComputeInstanceDelete(d *schema.ResourceData, meta interface{}) error { func resourceComputeInstanceDelete(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config) config := meta.(*Config)
op, err := config.clientCompute.Instances.Delete( zone := d.Get("zone").(string)
config.Project, d.Get("zone").(string), d.Id()).Do() op, err := config.clientCompute.Instances.Delete(config.Project, zone, d.Id()).Do()
if err != nil { if err != nil {
return fmt.Errorf("Error deleting instance: %s", err) return fmt.Errorf("Error deleting instance: %s", err)
} }
// Wait for the operation to complete // Wait for the operation to complete
w := &OperationWaiter{ opErr := resourceOperationWaitZone(config, op, zone, "instance to delete")
Service: config.clientCompute, if opErr != nil {
Op: op, return opErr
Project: config.Project,
Zone: d.Get("zone").(string),
Type: OperationWaitZone,
}
state := w.Conf()
state.Delay = 5 * time.Second
state.Timeout = 5 * time.Minute
state.MinTimeout = 2 * time.Second
opRaw, err := state.WaitForState()
if err != nil {
return fmt.Errorf("Error waiting for instance to delete: %s", err)
}
op = opRaw.(*compute.Operation)
if op.Error != nil {
// Return the error
return OperationError(*op.Error)
} }
d.SetId("") d.SetId("")
@ -577,16 +764,3 @@ func resourceInstanceTags(d *schema.ResourceData) *compute.Tags {
return tags return tags
} }
// resourceInstanceNatIP acquires the first NatIP with a "ONE_TO_ONE_NAT" type
// in the compute.NetworkInterface's AccessConfigs.
func resourceInstanceNatIP(iface *compute.NetworkInterface) (natIP string) {
for _, config := range iface.AccessConfigs {
if config.Type == "ONE_TO_ONE_NAT" {
natIP = config.NatIP
break
}
}
return natIP
}

View File

@ -10,6 +10,28 @@ import (
"github.com/hashicorp/terraform/terraform" "github.com/hashicorp/terraform/terraform"
) )
func TestAccComputeInstance_basic_deprecated_network(t *testing.T) {
var instance compute.Instance
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckComputeInstanceDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccComputeInstance_basic_deprecated_network,
Check: resource.ComposeTestCheckFunc(
testAccCheckComputeInstanceExists(
"google_compute_instance.foobar", &instance),
testAccCheckComputeInstanceTag(&instance, "foo"),
testAccCheckComputeInstanceMetadata(&instance, "foo", "bar"),
testAccCheckComputeInstanceDisk(&instance, "terraform-test", true, true),
),
},
},
})
}
func TestAccComputeInstance_basic(t *testing.T) { func TestAccComputeInstance_basic(t *testing.T) {
var instance compute.Instance var instance compute.Instance
@ -45,7 +67,7 @@ func TestAccComputeInstance_IP(t *testing.T) {
Check: resource.ComposeTestCheckFunc( Check: resource.ComposeTestCheckFunc(
testAccCheckComputeInstanceExists( testAccCheckComputeInstanceExists(
"google_compute_instance.foobar", &instance), "google_compute_instance.foobar", &instance),
testAccCheckComputeInstanceNetwork(&instance), testAccCheckComputeInstanceAccessConfigHasIP(&instance),
), ),
}, },
}, },
@ -73,6 +95,35 @@ func TestAccComputeInstance_disks(t *testing.T) {
}) })
} }
func TestAccComputeInstance_update_deprecated_network(t *testing.T) {
var instance compute.Instance
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckComputeInstanceDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccComputeInstance_basic_deprecated_network,
Check: resource.ComposeTestCheckFunc(
testAccCheckComputeInstanceExists(
"google_compute_instance.foobar", &instance),
),
},
resource.TestStep{
Config: testAccComputeInstance_update_deprecated_network,
Check: resource.ComposeTestCheckFunc(
testAccCheckComputeInstanceExists(
"google_compute_instance.foobar", &instance),
testAccCheckComputeInstanceMetadata(
&instance, "bar", "baz"),
testAccCheckComputeInstanceTag(&instance, "baz"),
),
},
},
})
}
func TestAccComputeInstance_update(t *testing.T) { func TestAccComputeInstance_update(t *testing.T) {
var instance compute.Instance var instance compute.Instance
@ -96,6 +147,7 @@ func TestAccComputeInstance_update(t *testing.T) {
testAccCheckComputeInstanceMetadata( testAccCheckComputeInstanceMetadata(
&instance, "bar", "baz"), &instance, "bar", "baz"),
testAccCheckComputeInstanceTag(&instance, "baz"), testAccCheckComputeInstanceTag(&instance, "baz"),
testAccCheckComputeInstanceAccessConfig(&instance),
), ),
}, },
}, },
@ -173,7 +225,19 @@ func testAccCheckComputeInstanceMetadata(
} }
} }
func testAccCheckComputeInstanceNetwork(instance *compute.Instance) resource.TestCheckFunc { func testAccCheckComputeInstanceAccessConfig(instance *compute.Instance) resource.TestCheckFunc {
return func(s *terraform.State) error {
for _, i := range instance.NetworkInterfaces {
if len(i.AccessConfigs) == 0 {
return fmt.Errorf("no access_config")
}
}
return nil
}
}
func testAccCheckComputeInstanceAccessConfigHasIP(instance *compute.Instance) resource.TestCheckFunc {
return func(s *terraform.State) error { return func(s *terraform.State) error {
for _, i := range instance.NetworkInterfaces { for _, i := range instance.NetworkInterfaces {
for _, c := range i.AccessConfigs { for _, c := range i.AccessConfigs {
@ -219,7 +283,7 @@ func testAccCheckComputeInstanceTag(instance *compute.Instance, n string) resour
} }
} }
const testAccComputeInstance_basic = ` const testAccComputeInstance_basic_deprecated_network = `
resource "google_compute_instance" "foobar" { resource "google_compute_instance" "foobar" {
name = "terraform-test" name = "terraform-test"
machine_type = "n1-standard-1" machine_type = "n1-standard-1"
@ -240,7 +304,7 @@ resource "google_compute_instance" "foobar" {
} }
}` }`
const testAccComputeInstance_update = ` const testAccComputeInstance_update_deprecated_network = `
resource "google_compute_instance" "foobar" { resource "google_compute_instance" "foobar" {
name = "terraform-test" name = "terraform-test"
machine_type = "n1-standard-1" machine_type = "n1-standard-1"
@ -260,6 +324,49 @@ resource "google_compute_instance" "foobar" {
} }
}` }`
const testAccComputeInstance_basic = `
resource "google_compute_instance" "foobar" {
name = "terraform-test"
machine_type = "n1-standard-1"
zone = "us-central1-a"
can_ip_forward = false
tags = ["foo", "bar"]
disk {
image = "debian-7-wheezy-v20140814"
}
network_interface {
network = "default"
}
metadata {
foo = "bar"
}
}`
// Update metadata, tags, and network_interface
const testAccComputeInstance_update = `
resource "google_compute_instance" "foobar" {
name = "terraform-test"
machine_type = "n1-standard-1"
zone = "us-central1-a"
tags = ["baz"]
disk {
image = "debian-7-wheezy-v20140814"
}
network_interface {
network = "default"
access_config { }
}
metadata {
bar = "baz"
}
}`
const testAccComputeInstance_ip = ` const testAccComputeInstance_ip = `
resource "google_compute_address" "foo" { resource "google_compute_address" "foo" {
name = "foo" name = "foo"
@ -275,9 +382,11 @@ resource "google_compute_instance" "foobar" {
image = "debian-7-wheezy-v20140814" image = "debian-7-wheezy-v20140814"
} }
network { network_interface {
source = "default" network = "default"
address = "${google_compute_address.foo.address}" access_config {
nat_ip = "${google_compute_address.foo.address}"
}
} }
metadata { metadata {
@ -307,8 +416,8 @@ resource "google_compute_instance" "foobar" {
auto_delete = false auto_delete = false
} }
network { network_interface {
source = "default" network = "default"
} }
metadata { metadata {

View File

@ -27,8 +27,11 @@ resource "google_compute_instance" "default" {
image = "debian-7-wheezy-v20140814" image = "debian-7-wheezy-v20140814"
} }
network { network_interface {
source = "default" network = "default"
access_config {
// Ephemeral IP
}
} }
metadata { metadata {
@ -64,7 +67,11 @@ The following arguments are supported:
* `metadata` - (Optional) Metadata key/value pairs to make available from * `metadata` - (Optional) Metadata key/value pairs to make available from
within the instance. within the instance.
* `network` - (Required) Networks to attach to the instance. This can be * `network_interface` - (Required) Networks to attach to the instance. This can be
specified multiple times for multiple networks. Structure is documented
below.
* `network` - (DEPRECATED, Required) Networks to attach to the instance. This can be
specified multiple times for multiple networks. Structure is documented specified multiple times for multiple networks. Structure is documented
below. below.
@ -85,7 +92,22 @@ The `disk` block supports:
* `type` - (Optional) The GCE disk type. * `type` - (Optional) The GCE disk type.
The `network` block supports: The `network_interface` block supports:
* `network` - (Required) The name of the network to attach this interface to.
* `access_config` - (Optional) Access configurations, i.e. IPs via which this instance can be
accessed via the Internet. Omit to ensure that the instance is not accessible from the Internet
(this means that ssh provisioners will not work unless you are running Terraform can send traffic to
the instance's network (e.g. via tunnel or because it is running on another cloud instance on that
network). This block can be repeated multiple times. Structure documented below.
The `access_config` block supports:
* `nat_ip` - (Optional) The IP address that will be 1:1 mapped to the instance's network ip. If not
given, one will be generated.
(DEPRECATED) The `network` block supports:
* `source` - (Required) The name of the network to attach this interface to. * `source` - (Required) The name of the network to attach this interface to.