github.com/danp/terraform@v0.9.5-0.20170426144147-39d740081351/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  		// The resource wasn't actually created
   109  		d.SetId("")
   110  		return waitErr
   111  	}
   112  
   113  	// Set the billing account
   114  	if v, ok := d.GetOk("billing_account"); ok {
   115  		name := v.(string)
   116  		ba := cloudbilling.ProjectBillingInfo{
   117  			BillingAccountName: "billingAccounts/" + name,
   118  		}
   119  		_, err = config.clientBilling.Projects.UpdateBillingInfo(prefixedProject(pid), &ba).Do()
   120  		if err != nil {
   121  			d.Set("billing_account", "")
   122  			if _err, ok := err.(*googleapi.Error); ok {
   123  				return fmt.Errorf("Error setting billing account %q for project %q: %v", name, prefixedProject(pid), _err)
   124  			}
   125  			return fmt.Errorf("Error setting billing account %q for project %q: %v", name, prefixedProject(pid), err)
   126  		}
   127  	}
   128  
   129  	return resourceGoogleProjectRead(d, meta)
   130  }
   131  
   132  func resourceGoogleProjectRead(d *schema.ResourceData, meta interface{}) error {
   133  	config := meta.(*Config)
   134  	pid := d.Id()
   135  
   136  	// Read the project
   137  	p, err := config.clientResourceManager.Projects.Get(pid).Do()
   138  	if err != nil {
   139  		if v, ok := err.(*googleapi.Error); ok && v.Code == http.StatusNotFound {
   140  			return fmt.Errorf("Project %q does not exist.", pid)
   141  		}
   142  		return fmt.Errorf("Error checking project %q: %s", pid, err)
   143  	}
   144  
   145  	d.Set("project_id", pid)
   146  	d.Set("number", strconv.FormatInt(int64(p.ProjectNumber), 10))
   147  	d.Set("name", p.Name)
   148  
   149  	if p.Parent != nil {
   150  		d.Set("org_id", p.Parent.Id)
   151  	}
   152  
   153  	// Read the billing account
   154  	ba, err := config.clientBilling.Projects.GetBillingInfo(prefixedProject(pid)).Do()
   155  	if err != nil {
   156  		return fmt.Errorf("Error reading billing account for project %q: %v", prefixedProject(pid), err)
   157  	}
   158  	if ba.BillingAccountName != "" {
   159  		// BillingAccountName is contains the resource name of the billing account
   160  		// associated with the project, if any. For example,
   161  		// `billingAccounts/012345-567890-ABCDEF`. We care about the ID and not
   162  		// the `billingAccounts/` prefix, so we need to remove that. If the
   163  		// prefix ever changes, we'll validate to make sure it's something we
   164  		// recognize.
   165  		_ba := strings.TrimPrefix(ba.BillingAccountName, "billingAccounts/")
   166  		if ba.BillingAccountName == _ba {
   167  			return fmt.Errorf("Error parsing billing account for project %q. Expected value to begin with 'billingAccounts/' but got %s", prefixedProject(pid), ba.BillingAccountName)
   168  		}
   169  		d.Set("billing_account", _ba)
   170  	}
   171  	return nil
   172  }
   173  
   174  func prefixedProject(pid string) string {
   175  	return "projects/" + pid
   176  }
   177  
   178  func resourceGoogleProjectUpdate(d *schema.ResourceData, meta interface{}) error {
   179  	config := meta.(*Config)
   180  	pid := d.Id()
   181  
   182  	// Read the project
   183  	// we need the project even though refresh has already been called
   184  	// because the API doesn't support patch, so we need the actual object
   185  	p, err := config.clientResourceManager.Projects.Get(pid).Do()
   186  	if err != nil {
   187  		if v, ok := err.(*googleapi.Error); ok && v.Code == http.StatusNotFound {
   188  			return fmt.Errorf("Project %q does not exist.", pid)
   189  		}
   190  		return fmt.Errorf("Error checking project %q: %s", pid, err)
   191  	}
   192  
   193  	// Project name has changed
   194  	if ok := d.HasChange("name"); ok {
   195  		p.Name = d.Get("name").(string)
   196  		// Do update on project
   197  		p, err = config.clientResourceManager.Projects.Update(p.ProjectId, p).Do()
   198  		if err != nil {
   199  			return fmt.Errorf("Error updating project %q: %s", p.Name, err)
   200  		}
   201  	}
   202  
   203  	// Billing account has changed
   204  	if ok := d.HasChange("billing_account"); ok {
   205  		name := d.Get("billing_account").(string)
   206  		ba := cloudbilling.ProjectBillingInfo{
   207  			BillingAccountName: "billingAccounts/" + name,
   208  		}
   209  		_, err = config.clientBilling.Projects.UpdateBillingInfo(prefixedProject(pid), &ba).Do()
   210  		if err != nil {
   211  			d.Set("billing_account", "")
   212  			if _err, ok := err.(*googleapi.Error); ok {
   213  				return fmt.Errorf("Error updating billing account %q for project %q: %v", name, prefixedProject(pid), _err)
   214  			}
   215  			return fmt.Errorf("Error updating billing account %q for project %q: %v", name, prefixedProject(pid), err)
   216  		}
   217  	}
   218  	return nil
   219  }
   220  
   221  func resourceGoogleProjectDelete(d *schema.ResourceData, meta interface{}) error {
   222  	config := meta.(*Config)
   223  	// Only delete projects if skip_delete isn't set
   224  	if !d.Get("skip_delete").(bool) {
   225  		pid := d.Id()
   226  		_, err := config.clientResourceManager.Projects.Delete(pid).Do()
   227  		if err != nil {
   228  			return fmt.Errorf("Error deleting project %q: %s", pid, err)
   229  		}
   230  	}
   231  	d.SetId("")
   232  	return nil
   233  }