func GetConfigMap() (map[string]interface{}, error) {
	cmd := exec.Command("gcloud", "config", "list", "--format=json")
	output, err := cmd.Output()
	if err != nil {
		return nil, fmt.Errorf("failed to list gcloud config : %v", err)
	}
	var result map[string]interface{}
	err = json.Unmarshal(output, &result)
	if err != nil {
		return nil, fmt.Errorf("failed to unmarshal gcloud config: %s : %v", string(output), err)
	}
	return result, nil
}
GetConfigMap returns all the gcloud config in a JSON struct.
package gcp
import (
	"encoding/json"
	"fmt"
	"os/exec"
	"strconv"
	"strings"
	"time"
)
const (
	oldSMCommandGroup = "service-management"
	newSMCommandGroup = "services"
	smChangeVersion   = "188.0.0"
)
func EnableAPIs(apis []string) error {
	existingAPIs, err := enabledAPIs()
	if err != nil {
		return err
	}
	for _, api := range apis {
		if _, found := existingAPIs[api]; !found {
			fmt.Printf("enabling a GCP API: %s\n", api)
			err = enableAPI(api)
			if err != nil {
				return err
			}
		}
	}
	return nil
}
func enabledAPIs() (map[string]bool, error) {
	cg, err := getCommandGroupByVersion(smChangeVersion, oldSMCommandGroup, newSMCommandGroup)
	if err != nil {
		return nil, fmt.Errorf("error retrieving command group for Service Management: %v", err)
	}
	cmd := exec.Command("gcloud", cg, "list", "--format", "json")
	output, err := cmd.Output()
	if err != nil {
		return nil, fmt.Errorf("failed to retrieve enabled GCP APIs : %v", err)
	}
	var apis []gcpAPI
	err = json.Unmarshal(output, &apis)
	if err != nil {
		return nil, fmt.Errorf("failed to parse enabled API response : %v", err)
	}
	m := make(map[string]bool, len(apis))
	for _, api := range apis {
		m[api.ServiceName] = true
	}
	return m, nil
}
type gcpAPI struct {
	ServiceName string `json:"serviceName"`
}
func enableAPI(api string) error {
	cg, err := getCommandGroupByVersion(smChangeVersion, oldSMCommandGroup, newSMCommandGroup)
	if err != nil {
		return fmt.Errorf("error retrieving command group for Service Management: %v", err)
	}
	cmd := exec.Command("gcloud", cg, "enable", api)
	_, err = cmd.CombinedOutput()
	if err != nil {
		return fmt.Errorf("failed to enable API %s : %v", api, err)
	}
	return nil
}
func getCommandGroupByVersion(version, oldCG, newCG string) (string, error) {
	cmd := exec.Command("gcloud", "version", "--format=json")
	output, err := cmd.Output()
	if err != nil {
		return "", fmt.Errorf("failed to retrieve Google Cloud SDK version: %s : %v", string(output), err)
	}
	versionMap := make(map[string]interface{})
	err = json.Unmarshal(output, &versionMap)
	if err != nil {
		return "", fmt.Errorf("error unmarshalling version result: %s : %v", string(output), err)
	}
	currentVersion := versionMap["Google Cloud SDK"].(string)
	cv, err := parseVersionToInt(currentVersion)
	if err != nil {
		return "", fmt.Errorf("error parsing version string to integer: %v", err)
	}
	v, err := parseVersionToInt(version)
	if err != nil {
		return "", fmt.Errorf("error parsing version string to integer: %v", err)
	}
	if cv < v {
		return oldCG, nil
	}
	return newCG, nil
}
func parseVersionToInt(version string) (int, error) {
	version = strings.Replace(version, ".", "", -1)
	return strconv.Atoi(version)
}
func CreateServiceAccount(name, displayName string) error {
	cmd := exec.Command("gcloud", "iam", "service-accounts", "create",
		name,
		"--display-name", displayName,
		"--format", "json")
	output, err := cmd.CombinedOutput()
	if err != nil {
		return fmt.Errorf("failed to create service account : %v %s", err, string(output))
	}
	return err
}
func GetServiceAccount(email string) (*ServiceAccount, error) {
	cmd := exec.Command("gcloud", "iam", "service-accounts", "describe", email, "--format", "json")
	output, err := cmd.Output()
	if err != nil {
		return nil, fmt.Errorf("failed to retrieve service account : %v:%v", err, string(output))
	}
	var sa ServiceAccount
	err = json.Unmarshal(output, &sa)
	if err != nil {
		return nil, fmt.Errorf("failed to parse service account API response : %v", err)
	}
	return &sa, nil
}
func AddServiceAccountPerms(projectID, email, roles string) error {
	cmd := exec.Command("gcloud", "projects", "add-iam-policy-binding", projectID, "--member", "serviceAccount:"+email, "--role", roles, "--format", "json")
	output, err := cmd.CombinedOutput()
	if err != nil {
		return fmt.Errorf("failed to add service account permissions: %v %s", string(output), err)
	}
	return nil
}
func RemoveServiceAccountPerms(projectID, email, roles string) error {
	cmd := exec.Command("gcloud", "projects", "remove-iam-policy-binding", projectID, "--member", "serviceAccount:"+email, "--role", roles, "--format", "json")
	output, err := cmd.CombinedOutput()
	if err != nil {
		return fmt.Errorf("failed to remove service account permissions: %v %s", string(output), err)
	}
	return nil
}
type ServiceAccount struct {
	Email       string `json:"email"`
	Name        string `json:"name"`
	DisplayName string `json:"displayName"`
}
func CreateServiceAccountKey(email, keyFilepath string) error {
	cmd := exec.Command("gcloud", "iam", "service-accounts", "keys", "create", "--iam-account", email, keyFilepath)
	out, err := cmd.CombinedOutput()
	if err != nil {
		return fmt.Errorf("failed to create service account key: %s : %v", string(out), err)
	}
	return nil
}
type saKey struct {
	Algorithm       string `json:"keyAlgorithm"`
	Name            string `json:"name"`
	ValidAfterTime  string `json:"validAfterTime"`
	ValidBeforeTime string `json:"validBeforeTime"`
}
func RemoveAllServiceAccountKeys(email string) error {
	cmd := exec.Command("gcloud", "iam", "service-accounts", "keys", "list", "--iam-account", email, "--format=json")
	out, err := cmd.CombinedOutput()
	if err != nil {
		return fmt.Errorf("failed to list service account keys: %s : %v", string(out), err)
	}
	var keys []saKey
	err = json.Unmarshal(out, &keys)
	if err != nil {
		return fmt.Errorf("failed to unmarshal service account keys: %s : %v", string(out), err)
	}
	for _, k := range keys {
		bt, err := time.Parse(time.RFC3339, k.ValidAfterTime)
		if err != nil {
			return fmt.Errorf("failed to parse the timestamp of the service account key (%+v): %v", k, err)
		}
		et, err := time.Parse(time.RFC3339, k.ValidBeforeTime)
		if err != nil {
			return fmt.Errorf("failed to parse the timestamp of the service account key (%+v): %v", k, err)
		}
		life := et.Sub(bt)
		if life > 365*24*time.Hour {
			keyID := strings.Split(k.Name, "/")[strings.Count(k.Name, "/")]
			RemoveServiceAccountKey(email, keyID)
		}
	}
	return nil
}
func RemoveServiceAccountKey(email, keyID string) {
	cmd := exec.Command("gcloud", "iam", "service-accounts", "keys", "delete", keyID, "--iam-account", email, "--quiet"  )
	out, err := cmd.CombinedOutput()
	if err != nil {
		fmt.Printf("failed to delete service account key: %s : %v\n", string(out), err)
		fmt.Printf("WARNING: Please clean up the key from service account %s", email)
	}
}
func GetConfigValue(section, property string) (string, error) {
	cmd := exec.Command("gcloud", "config", "get-value", section+"/"+property)
	value, err := cmd.Output()
	if err != nil {
		return "", fmt.Errorf("failed to retrieve config-value : %v", err)
	}
	return strings.Trim(string(value), "\n"), nil
}