github.com/peterbale/terraform@v0.9.0-beta2.0.20170315142748-5723acd55547/builtin/providers/google/resource_google_project.go (about)

     1  package google
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  	"net/http"
     7  	"strconv"
     8  	"strings"
     9  
    10  	"github.com/hashicorp/terraform/helper/schema"
    11  	"google.golang.org/api/cloudbilling/v1"
    12  	"google.golang.org/api/cloudresourcemanager/v1"
    13  	"google.golang.org/api/googleapi"
    14  )
    15  
    16  // resourceGoogleProject returns a *schema.Resource that allows a customer
    17  // to declare a Google Cloud Project resource.
    18  func resourceGoogleProject() *schema.Resource {
    19  	return &schema.Resource{
    20  		SchemaVersion: 1,
    21  
    22  		Create: resourceGoogleProjectCreate,
    23  		Read:   resourceGoogleProjectRead,
    24  		Update: resourceGoogleProjectUpdate,
    25  		Delete: resourceGoogleProjectDelete,
    26  
    27  		Importer: &schema.ResourceImporter{
    28  			State: schema.ImportStatePassthrough,
    29  		},
    30  		MigrateState: resourceGoogleProjectMigrateState,
    31  
    32  		Schema: map[string]*schema.Schema{
    33  			"id": &schema.Schema{
    34  				Type:     schema.TypeString,
    35  				Optional: true,
    36  				Computed: true,
    37  				Removed:  "The id field has been removed. Use project_id instead.",
    38  			},
    39  			"project_id": &schema.Schema{
    40  				Type:     schema.TypeString,
    41  				Required: true,
    42  				ForceNew: true,
    43  			},
    44  			"skip_delete": &schema.Schema{
    45  				Type:     schema.TypeBool,
    46  				Optional: true,
    47  				Computed: true,
    48  			},
    49  			"name": &schema.Schema{
    50  				Type:     schema.TypeString,
    51  				Required: true,
    52  			},
    53  			"org_id": &schema.Schema{
    54  				Type:     schema.TypeString,
    55  				Required: true,
    56  				ForceNew: true,
    57  			},
    58  			"policy_data": &schema.Schema{
    59  				Type:     schema.TypeString,
    60  				Optional: true,
    61  				Computed: true,
    62  				Removed:  "Use the 'google_project_iam_policy' resource to define policies for a Google Project",
    63  			},
    64  			"policy_etag": &schema.Schema{
    65  				Type:     schema.TypeString,
    66  				Computed: true,
    67  				Removed:  "Use the the 'google_project_iam_policy' resource to define policies for a Google Project",
    68  			},
    69  			"number": &schema.Schema{
    70  				Type:     schema.TypeString,
    71  				Computed: true,
    72  			},
    73  			"billing_account": &schema.Schema{
    74  				Type:     schema.TypeString,
    75  				Optional: true,
    76  			},
    77  		},
    78  	}
    79  }
    80  
    81  func resourceGoogleProjectCreate(d *schema.ResourceData, meta interface{}) error {
    82  	config := meta.(*Config)
    83  
    84  	var pid string
    85  	var err error
    86  	pid = d.Get("project_id").(string)
    87  
    88  	log.Printf("[DEBUG]: Creating new project %q", pid)
    89  	project := &cloudresourcemanager.Project{
    90  		ProjectId: pid,
    91  		Name:      d.Get("name").(string),
    92  		Parent: &cloudresourcemanager.ResourceId{
    93  			Id:   d.Get("org_id").(string),
    94  			Type: "organization",
    95  		},
    96  	}
    97  
    98  	op, err := config.clientResourceManager.Projects.Create(project).Do()
    99  	if err != nil {
   100  		return fmt.Errorf("Error creating project %s (%s): %s.", project.ProjectId, project.Name, err)
   101  	}
   102  
   103  	d.SetId(pid)
   104  
   105  	// Wait for the operation to complete
   106  	waitErr := resourceManagerOperationWait(config, op, "project to create")
   107  	if waitErr != nil {
   108  		return waitErr
   109  	}
   110  
   111  	// Set the billing account
   112  	if v, ok := d.GetOk("billing_account"); ok {
   113  		name := v.(string)
   114  		ba := cloudbilling.ProjectBillingInfo{
   115  			BillingAccountName: "billingAccounts/" + name,
   116  		}
   117  		_, err = config.clientBilling.Projects.UpdateBillingInfo(prefixedProject(pid), &ba).Do()
   118  		if err != nil {
   119  			d.Set("billing_account", "")
   120  			if _err, ok := err.(*googleapi.Error); ok {
   121  				return fmt.Errorf("Error setting billing account %q for project %q: %v", name, prefixedProject(pid), _err)
   122  			}
   123  			return fmt.Errorf("Error setting billing account %q for project %q: %v", name, prefixedProject(pid), err)
   124  		}
   125  	}
   126  
   127  	return resourceGoogleProjectRead(d, meta)
   128  }
   129  
   130  func resourceGoogleProjectRead(d *schema.ResourceData, meta interface{}) error {
   131  	config := meta.(*Config)
   132  	pid := d.Id()
   133  
   134  	// Read the project
   135  	p, err := config.clientResourceManager.Projects.Get(pid).Do()
   136  	if err != nil {
   137  		if v, ok := err.(*googleapi.Error); ok && v.Code == http.StatusNotFound {
   138  			return fmt.Errorf("Project %q does not exist.", pid)
   139  		}
   140  		return fmt.Errorf("Error checking project %q: %s", pid, err)
   141  	}
   142  
   143  	d.Set("project_id", pid)
   144  	d.Set("number", strconv.FormatInt(int64(p.ProjectNumber), 10))
   145  	d.Set("name", p.Name)
   146  
   147  	if p.Parent != nil {
   148  		d.Set("org_id", p.Parent.Id)
   149  	}
   150  
   151  	// Read the billing account
   152  	ba, err := config.clientBilling.Projects.GetBillingInfo(prefixedProject(pid)).Do()
   153  	if err != nil {
   154  		return fmt.Errorf("Error reading billing account for project %q: %v", prefixedProject(pid), err)
   155  	}
   156  	if ba.BillingAccountName != "" {
   157  		// BillingAccountName is contains the resource name of the billing account
   158  		// associated with the project, if any. For example,
   159  		// `billingAccounts/012345-567890-ABCDEF`. We care about the ID and not
   160  		// the `billingAccounts/` prefix, so we need to remove that. If the
   161  		// prefix ever changes, we'll validate to make sure it's something we
   162  		// recognize.
   163  		_ba := strings.TrimPrefix(ba.BillingAccountName, "billingAccounts/")
   164  		if ba.BillingAccountName == _ba {
   165  			return fmt.Errorf("Error parsing billing account for project %q. Expected value to begin with 'billingAccounts/' but got %s", prefixedProject(pid), ba.BillingAccountName)
   166  		}
   167  		d.Set("billing_account", _ba)
   168  	}
   169  	return nil
   170  }
   171  
   172  func prefixedProject(pid string) string {
   173  	return "projects/" + pid
   174  }
   175  
   176  func resourceGoogleProjectUpdate(d *schema.ResourceData, meta interface{}) error {
   177  	config := meta.(*Config)
   178  	pid := d.Id()
   179  
   180  	// Read the project
   181  	// we need the project even though refresh has already been called
   182  	// because the API doesn't support patch, so we need the actual object
   183  	p, err := config.clientResourceManager.Projects.Get(pid).Do()
   184  	if err != nil {
   185  		if v, ok := err.(*googleapi.Error); ok && v.Code == http.StatusNotFound {
   186  			return fmt.Errorf("Project %q does not exist.", pid)
   187  		}
   188  		return fmt.Errorf("Error checking project %q: %s", pid, err)
   189  	}
   190  
   191  	// Project name has changed
   192  	if ok := d.HasChange("name"); ok {
   193  		p.Name = d.Get("name").(string)
   194  		// Do update on project
   195  		p, err = config.clientResourceManager.Projects.Update(p.ProjectId, p).Do()
   196  		if err != nil {
   197  			return fmt.Errorf("Error updating project %q: %s", p.Name, err)
   198  		}
   199  	}
   200  
   201  	// Billing account has changed
   202  	if ok := d.HasChange("billing_account"); ok {
   203  		name := d.Get("billing_account").(string)
   204  		ba := cloudbilling.ProjectBillingInfo{
   205  			BillingAccountName: "billingAccounts/" + name,
   206  		}
   207  		_, err = config.clientBilling.Projects.UpdateBillingInfo(prefixedProject(pid), &ba).Do()
   208  		if err != nil {
   209  			d.Set("billing_account", "")
   210  			if _err, ok := err.(*googleapi.Error); ok {
   211  				return fmt.Errorf("Error updating billing account %q for project %q: %v", name, prefixedProject(pid), _err)
   212  			}
   213  			return fmt.Errorf("Error updating billing account %q for project %q: %v", name, prefixedProject(pid), err)
   214  		}
   215  	}
   216  	return nil
   217  }
   218  
   219  func resourceGoogleProjectDelete(d *schema.ResourceData, meta interface{}) error {
   220  	config := meta.(*Config)
   221  	// Only delete projects if skip_delete isn't set
   222  	if !d.Get("skip_delete").(bool) {
   223  		pid := d.Id()
   224  		_, err := config.clientResourceManager.Projects.Delete(pid).Do()
   225  		if err != nil {
   226  			return fmt.Errorf("Error deleting project %q: %s", pid, err)
   227  		}
   228  	}
   229  	d.SetId("")
   230  	return nil
   231  }