2016-08-10 06:44:53 +02:00
package google
import (
"fmt"
"log"
"net/http"
"strconv"
2017-02-20 18:32:24 +01:00
"strings"
2016-08-10 06:44:53 +02:00
"github.com/hashicorp/terraform/helper/schema"
2017-02-20 18:32:24 +01:00
"google.golang.org/api/cloudbilling/v1"
2016-08-10 06:44:53 +02:00
"google.golang.org/api/cloudresourcemanager/v1"
"google.golang.org/api/googleapi"
)
2016-08-21 09:25:00 +02:00
// resourceGoogleProject returns a *schema.Resource that allows a customer
2016-11-23 07:55:40 +01:00
// to declare a Google Cloud Project resource.
2016-08-10 06:44:53 +02:00
func resourceGoogleProject ( ) * schema . Resource {
return & schema . Resource {
2016-11-23 07:55:40 +01:00
SchemaVersion : 1 ,
2016-08-10 06:44:53 +02:00
Create : resourceGoogleProjectCreate ,
Read : resourceGoogleProjectRead ,
Update : resourceGoogleProjectUpdate ,
Delete : resourceGoogleProjectDelete ,
2016-11-23 07:55:40 +01:00
2016-12-01 19:38:27 +01:00
Importer : & schema . ResourceImporter {
State : schema . ImportStatePassthrough ,
} ,
2016-11-23 07:55:40 +01:00
MigrateState : resourceGoogleProjectMigrateState ,
2016-08-10 06:44:53 +02:00
Schema : map [ string ] * schema . Schema {
2016-08-23 22:34:54 +02:00
"id" : & schema . Schema {
2017-03-14 00:19:53 +01:00
Type : schema . TypeString ,
Optional : true ,
Computed : true ,
Removed : "The id field has been removed. Use project_id instead." ,
2016-11-23 07:55:40 +01:00
} ,
"project_id" : & schema . Schema {
2016-08-10 06:44:53 +02:00
Type : schema . TypeString ,
2017-03-14 00:19:53 +01:00
Required : true ,
2016-08-10 06:44:53 +02:00
ForceNew : true ,
} ,
2016-11-23 07:55:40 +01:00
"skip_delete" : & schema . Schema {
Type : schema . TypeBool ,
2016-08-10 06:44:53 +02:00
Optional : true ,
2016-11-23 07:55:40 +01:00
Computed : true ,
2016-08-10 06:44:53 +02:00
} ,
"name" : & schema . Schema {
Type : schema . TypeString ,
2017-03-14 00:19:53 +01:00
Required : true ,
2016-08-10 06:44:53 +02:00
} ,
2016-11-23 07:55:40 +01:00
"org_id" : & schema . Schema {
Type : schema . TypeString ,
2017-03-14 00:19:53 +01:00
Required : true ,
2016-11-23 07:55:40 +01:00
ForceNew : true ,
} ,
"policy_data" : & schema . Schema {
2017-03-14 00:19:53 +01:00
Type : schema . TypeString ,
Optional : true ,
Computed : true ,
Removed : "Use the 'google_project_iam_policy' resource to define policies for a Google Project" ,
2016-11-23 07:55:40 +01:00
} ,
"policy_etag" : & schema . Schema {
2017-03-14 00:19:53 +01:00
Type : schema . TypeString ,
Computed : true ,
Removed : "Use the the 'google_project_iam_policy' resource to define policies for a Google Project" ,
2016-11-23 07:55:40 +01:00
} ,
2016-08-10 06:44:53 +02:00
"number" : & schema . Schema {
Type : schema . TypeString ,
Computed : true ,
} ,
2017-02-20 18:32:24 +01:00
"billing_account" : & schema . Schema {
Type : schema . TypeString ,
Optional : true ,
} ,
2016-08-10 06:44:53 +02:00
} ,
}
}
func resourceGoogleProjectCreate ( d * schema . ResourceData , meta interface { } ) error {
config := meta . ( * Config )
2016-11-23 07:55:40 +01:00
var pid string
var err error
pid = d . Get ( "project_id" ) . ( string )
log . Printf ( "[DEBUG]: Creating new project %q" , pid )
project := & cloudresourcemanager . Project {
ProjectId : pid ,
Name : d . Get ( "name" ) . ( string ) ,
Parent : & cloudresourcemanager . ResourceId {
Id : d . Get ( "org_id" ) . ( string ) ,
Type : "organization" ,
} ,
}
op , err := config . clientResourceManager . Projects . Create ( project ) . Do ( )
2016-08-10 06:44:53 +02:00
if err != nil {
2016-11-23 07:55:40 +01:00
return fmt . Errorf ( "Error creating project %s (%s): %s." , project . ProjectId , project . Name , err )
2016-08-10 06:44:53 +02:00
}
2016-11-23 07:55:40 +01:00
d . SetId ( pid )
// Wait for the operation to complete
waitErr := resourceManagerOperationWait ( config , op , "project to create" )
if waitErr != nil {
2017-04-14 01:16:47 +02:00
// The resource wasn't actually created
d . SetId ( "" )
2016-11-23 07:55:40 +01:00
return waitErr
2016-08-10 06:44:53 +02:00
}
2017-02-20 18:32:24 +01:00
// Set the billing account
if v , ok := d . GetOk ( "billing_account" ) ; ok {
name := v . ( string )
ba := cloudbilling . ProjectBillingInfo {
BillingAccountName : "billingAccounts/" + name ,
}
_ , err = config . clientBilling . Projects . UpdateBillingInfo ( prefixedProject ( pid ) , & ba ) . Do ( )
if err != nil {
d . Set ( "billing_account" , "" )
if _err , ok := err . ( * googleapi . Error ) ; ok {
return fmt . Errorf ( "Error setting billing account %q for project %q: %v" , name , prefixedProject ( pid ) , _err )
}
return fmt . Errorf ( "Error setting billing account %q for project %q: %v" , name , prefixedProject ( pid ) , err )
}
}
2016-11-23 07:55:40 +01:00
return resourceGoogleProjectRead ( d , meta )
2016-08-10 06:44:53 +02:00
}
func resourceGoogleProjectRead ( d * schema . ResourceData , meta interface { } ) error {
config := meta . ( * Config )
2016-11-23 07:55:40 +01:00
pid := d . Id ( )
2016-08-10 06:44:53 +02:00
2016-11-23 07:55:40 +01:00
// Read the project
p , err := config . clientResourceManager . Projects . Get ( pid ) . Do ( )
2016-08-10 06:44:53 +02:00
if err != nil {
if v , ok := err . ( * googleapi . Error ) ; ok && v . Code == http . StatusNotFound {
2016-11-23 07:55:40 +01:00
return fmt . Errorf ( "Project %q does not exist." , pid )
2016-08-10 06:44:53 +02:00
}
2016-11-23 07:55:40 +01:00
return fmt . Errorf ( "Error checking project %q: %s" , pid , err )
2016-08-10 06:44:53 +02:00
}
2016-11-23 07:55:40 +01:00
d . Set ( "project_id" , pid )
2016-08-10 06:44:53 +02:00
d . Set ( "number" , strconv . FormatInt ( int64 ( p . ProjectNumber ) , 10 ) )
d . Set ( "name" , p . Name )
2016-11-23 07:55:40 +01:00
if p . Parent != nil {
d . Set ( "org_id" , p . Parent . Id )
}
2017-02-20 18:32:24 +01:00
// Read the billing account
ba , err := config . clientBilling . Projects . GetBillingInfo ( prefixedProject ( pid ) ) . Do ( )
if err != nil {
return fmt . Errorf ( "Error reading billing account for project %q: %v" , prefixedProject ( pid ) , err )
}
if ba . BillingAccountName != "" {
// BillingAccountName is contains the resource name of the billing account
// associated with the project, if any. For example,
// `billingAccounts/012345-567890-ABCDEF`. We care about the ID and not
// the `billingAccounts/` prefix, so we need to remove that. If the
// prefix ever changes, we'll validate to make sure it's something we
// recognize.
_ba := strings . TrimPrefix ( ba . BillingAccountName , "billingAccounts/" )
if ba . BillingAccountName == _ba {
return fmt . Errorf ( "Error parsing billing account for project %q. Expected value to begin with 'billingAccounts/' but got %s" , prefixedProject ( pid ) , ba . BillingAccountName )
}
d . Set ( "billing_account" , _ba )
}
2016-08-10 06:44:53 +02:00
return nil
}
2017-02-20 18:32:24 +01:00
func prefixedProject ( pid string ) string {
return "projects/" + pid
}
2017-03-14 00:19:53 +01:00
2016-08-10 06:44:53 +02:00
func resourceGoogleProjectUpdate ( d * schema . ResourceData , meta interface { } ) error {
config := meta . ( * Config )
2016-11-23 07:55:40 +01:00
pid := d . Id ( )
// Read the project
// we need the project even though refresh has already been called
// because the API doesn't support patch, so we need the actual object
p , err := config . clientResourceManager . Projects . Get ( pid ) . Do ( )
2016-08-10 06:44:53 +02:00
if err != nil {
2016-11-23 07:55:40 +01:00
if v , ok := err . ( * googleapi . Error ) ; ok && v . Code == http . StatusNotFound {
return fmt . Errorf ( "Project %q does not exist." , pid )
}
return fmt . Errorf ( "Error checking project %q: %s" , pid , err )
}
// Project name has changed
if ok := d . HasChange ( "name" ) ; ok {
p . Name = d . Get ( "name" ) . ( string )
// Do update on project
p , err = config . clientResourceManager . Projects . Update ( p . ProjectId , p ) . Do ( )
if err != nil {
return fmt . Errorf ( "Error updating project %q: %s" , p . Name , err )
}
}
2017-02-20 18:32:24 +01:00
// Billing account has changed
if ok := d . HasChange ( "billing_account" ) ; ok {
name := d . Get ( "billing_account" ) . ( string )
ba := cloudbilling . ProjectBillingInfo {
BillingAccountName : "billingAccounts/" + name ,
}
_ , err = config . clientBilling . Projects . UpdateBillingInfo ( prefixedProject ( pid ) , & ba ) . Do ( )
if err != nil {
d . Set ( "billing_account" , "" )
if _err , ok := err . ( * googleapi . Error ) ; ok {
return fmt . Errorf ( "Error updating billing account %q for project %q: %v" , name , prefixedProject ( pid ) , _err )
}
return fmt . Errorf ( "Error updating billing account %q for project %q: %v" , name , prefixedProject ( pid ) , err )
}
}
2017-03-14 00:19:53 +01:00
return nil
2016-11-23 07:55:40 +01:00
}
func resourceGoogleProjectDelete ( d * schema . ResourceData , meta interface { } ) error {
config := meta . ( * Config )
// Only delete projects if skip_delete isn't set
if ! d . Get ( "skip_delete" ) . ( bool ) {
pid := d . Id ( )
_ , err := config . clientResourceManager . Projects . Delete ( pid ) . Do ( )
if err != nil {
return fmt . Errorf ( "Error deleting project %q: %s" , pid , err )
}
2016-08-10 06:44:53 +02:00
}
2016-11-23 07:55:40 +01:00
d . SetId ( "" )
return nil
}