 
         @_gcloud = require('gcloud')(projectId: PROJECT)
         @_gce    = @_gcloud.compute()
+
+    create_vm: (opts) =>
+        opts = defaults opts,
+            name         : required
+            zone         : DEFAULT_ZONE
+            disks        : undefined      # see disks[] at https://cloud.google.com/compute/docs/reference/latest/instances
+                                          # can also pass in Disk objects in the array; or a single string which
+                                          # will refer to the disk with that name in same zone.
+            http         : undefined      # allow http
+            https        : undefined      # allow https
+            machine_type : undefined      # the instance type, e.g., 'n1-standard-1'
+            os           : undefined      # see https://github.com/stephenplusplus/gce-images#accepted-os-names
+            tags         : undefined      # array of strings
+            preemptible  : false
+            cb           : required
+        dbg = @dbg("create_vm(name=#{opts.name})")
+        config = {}
+        config.http        = opts.http if opts.http?
+        config.https       = opts.https if opts.https?
+        config.machineType = opts.machine_type if opts.machine_type?
+        config.os          = opts.os if opts.os?
+        config.tags        = opts.tags if opts.tags?
+        if opts.preemptible
+            config.scheduling = {preemptible : true}
+        if opts.disks?
+            config.disks = []
+            for disk in opts.disks
+                if typeof(disk) == 'string'
+                    disk = @disk(name:disk, zone:opts.zone)  # gets used immediately below!
+
+                if disk instanceof Disk
+                    # use existing disk read/write
+                    config.disks.push({source:disk._disk.formattedName})
+                else
+                    # use object as specified at https://cloud.google.com/compute/docs/reference/latest/instances
+                    config.disks.push(disk)
+            # ensure at least one disk is a boot disk
+            if config.disks.length > 0 and (x for x in config.disks when x.boot).length == 0
+                config.disks[0].boot = true
+        dbg("config=#{misc.to_json(config)}")
+        @_gce.zone(opts.zone).createVM opts.name, config, (err, vm, operation, apiResponse) =>
+            handle_operation(err, operation, (->dbg('done')), opts.cb)
 
     vm: (opts) =>
         opts = defaults opts,

                disk = @gcloud._gce.zone(@zone).disk(deviceName)
                @_vm.detachDisk disk, (err, operation, apiResponse) =>
                    handle_operation(err, operation, (->dbg("done")), cb)
        ], opts.cb)

class Disk
    constructor: (@gcloud, @name, @zone=DEFAULT_ZONE) ->


    attach_disk: (opts) =>
        opts = defaults opts,
            disk      : required
            read_only : false
            cb        : required
        dbg = @dbg("attach_disk")
        if not (opts.disk instanceof Disk)
            dbg("not Disk")
            if typeof(opts.disk) == 'string'
                dbg("is string so make disk")
                opts.disk = @gcloud.disk(name:opts.disk, zone:@zone)
            else
                opts.cb("disk must be an instance of Disk")
                return
        dbg("starting...")
        @_vm.attachDisk opts.disk._disk, {readOnly: opts.read_only}, (err, operation, apiResponse) =>
            handle_operation(err, operation, (->dbg("done")), opts.cb)

    detach_disk: (opts) =>
        opts = defaults opts,
            disk : required
            cb   : required
        dbg = @dbg("detach_disk")
        if not (opts.disk instanceof Disk)
            dbg("not Disk")
            if typeof(opts.disk) == 'string'
                dbg("is string so make disk")
                opts.disk = @gcloud.disk(name:opts.disk, zone:@zone)
            else
                opts.cb("disk must be an instance of Disk")
                return
        dbg("starting...")
        vm_data = disk_data = undefined
        async.series([
