Skip to main content

Cloud : Processing guide

S
Written by Support

1. Align with the client’s specific requests and choose the most suited module🎯Examples of common requests

  • Client not wanting to connect through an API

  • Client not having detailed data
    How to chose the most suited module format?

  • 100% automatic processing → End-to-end module
    The use of e2e module should always be privileged. The first step when a client doesn’t want to connect using API is to understand why, send our security policy and see if a solution can be found (including connecting data for only a very limited laps of time and then deactivate the API).
    When a client doesn’t have detailed physical data, and/or uses a smaller cloud provider, we can still precise their emissions with a lightweight monetary module. The data to be included is the country of servers and amount spent for each service (compute, storage, transfer).

  • Manual processing → Advanced module
    This kind of module is to be used only when there is a major blocker in the API connections, or for others suppliers (validate with the AM if you need to use an advanced module for one of the big 3 providers, as it require more manual processing and might need to be billed)

2. Process the data

Pre-processing checks (all paths)

Before processing, run these checks on the data received from the client:

  1. Expense consistency: Confirm the total spend in the file matches what is visible on the platform.

  2. Service distribution: In E2E modules, verify that no single service type (compute, storage, transfer) represents more than 95% of total emissions — each should be between 10%–50%. If one dominates, check whether the service name is correctly categorized and whether the unit and quantity are correct.

  3. Year-on-year comparison: If applicable, compare with the previous year to verify the evolution is consistent or can be explained.

  4. Order of magnitude check: Calculate a monetary ratio (carbon impact ÷ expense). The result should be between 0.05 and 0.6 kgCO2e/EUR.

Data processing###Automatic processing (E2E module)

The processing is automated, you only have to check the data.

Analysis Templates

A google sheet template is also available for each provider: AWS, Azure, GCP, Other.

Manual processing - Other

  • Go to the template model

  • Copy the data from your data collection template to the “Bill - Data source” tab (subscription name and ID aren’t mandatory)

  • Make sure to fill the column “Type” as it is used to put data in the right tab

  • The orange tabs “Compute”, “Storage”, “Transfer” are filled without errors. Computations should be done automatically everywhere

  • The tab “Export auditable Plateforme” will give you the right table to import results on the platform

Manual processing - OVH

  • The client will send their document (see data collection process)

  • Whenever you receive this document:

  • Store it in your client’s folder

  • Create 3 company EFs, using the Emission Factor Manager, for:
    - Manufacturing
    - Electricity (location based)
    - Operations
    With the following parameters:

  • Name EN: OVH Cloud | Manufacturing/Electricity/Operations | YEAR | Client’s name

  • Name FR: OVH Cloud | Fabrication/Electricité/Opérations | YEAR | Client’s name

  • Value: 1

  • Unit: UNIT

  • Methodology EN: “This emission factor is used to input emissions from OVH carbon reporting. For more information, refer to the document linked in the methodology notes”

  • Methodology FR: “Ce facteur d'émission est utilisé pour saisir les émissions du reporting carbone d'OVH. Pour plus d'informations, se référer au document lié dans les notes méthodologiques”

  • Company: name of the company

  • ReferentialType: Other

  • PurchaseCategory: CLOUD_SERVER

  • Year: Year of the assessment

  • LifeCycleStages: All but “end-of-life”

  • ConfidenceScore: 0.7

  • Uncertainty: 30%

  • MethodologyNote: Link to the carbon reporting in your client’s folder

Regulatory entries

{
"BEGESv4": {
"9": 100
},
"BEGESv5": {
"4.5": 100
},
"GHGProtocol": {
"3.1": 100
}
}

  • Use these EFs in a custom module, quantity being the number of kgCO2e for each subcategory.
    ⚠️ The quantity should be taken from the top summary table, and not from the details of service table (split of the summary table into different services given to the client)

Manual processing - AWS - with carbon footprint export####Step 1*: Download data shared by the client*

The file should look like this. Make sure the model version is v3.0

Step 2: Prepare the company EFs

Go to this file (WiP), make a copy of it, and copy your data in the “bill example” tab. Only keep the lines for the right timeframe (column usage month)
In the “Template” tab, fill the information in the first column (company ID and link to the file)
Then, make a R& M request to create the company EFs (category module→ Cloud).

Step 3: Import the created EFs

Once the emission factors are created, import them in an advanced module, with quantity = 1 and unit = UNIT.

Manual processing - AWS - with Greenly model####Step 1*: Go to the template file*

All the work will be done ON the template file, and only at the end you can copy it to your client’s folder.
This ensures any change / addition to the model will be kept for future studies.
Note: If you NEED to do any specific tweaks for this particular studies and do not want to keep them for future studies, simply do them after you copied the template file.

Step 2: Import your data

There will be the last study data. You need to first delete this source data (make sure to delete everything, and not have some remaining data get into your new study)
Paste your client’s data to the tab “AWS - Données Sources”. At the moment, we do this by Ctrl+C/V from one file to another. Not optimal, but does the trick.
The data is the usage description, price, volume, region, and month.
Give the file some time to “ingest” the new data (It runs every formula on the sheet for the data you just pasted)
Check the total number of rows in the source file, and in the template (Feels dumb to say it, but it happens)

Step 3: Verify unrecognized lines and categorization of services

The next step is to feed the model (see below) to take account of unrecognized lines.
Get to the “Split usages” tab.
This is where you can add keywords to classify rows.
The idea is to split rows into a category:
Computation, Storage, Transfer, Other Services or No Impact

This is a two step process:
First, understand what this row means, and which category you would put it into, by searching the Web
Second, see if the way the info is provided (What unit is given? Is it something weird or can we trace it back to physical activities) is sufficient. If not, other services is plan B.

No impact is purely for taxes / other monetary factors. If there is cloud activities, the default category is other services.
The key metric is the %age of expenses which are categorised by the keywords. It is the colored cell in L2: You need to lower it under 5%, at which point it turns green.
Also make sure there is no major double counting (i.e. some rows having keywords of two categories, and being counted in both). This is shown on columns M and N.

Step 4: Verify the analysis and results consistency

Check the Results tab and other tabs of this color for any unusual thing that pops out in the graphs or indicators.

A good way to do so is to check orders of magnitudes on emissions per billed amount:

For computation, it should vary between 0.02 and 0.2, depending on the carbon intensity and overall volume (the more volume the higher this number gets, because of bundle prices)
Same for storage
For transfer, this number can get bigger, as internet transfer is paid by the client, not the cloud provider
If anything seems wrong in the computation tab, it can be because of unit conversion problems. This is handled in the Computation Data tab.

Step 5 Copy the template file to the client’s folder

The analysis of the data is done, no need to work on the template anymore. Make a copy of the document in your client folder, and rename it.
On the new file, you need to give access to files linked to the template by import ranges
Simply go on the result tab, click on errors, and allow access:


It may take some time (a few minutes)
To make sure everything went well, check the Computation methodo tab

There has to be different values in columns E and F just like in this screenshot (and not just 2s in E and the mean value in F)

If you’re having trouble, sometimes it’s necessary to cut the formula in D40 and press enter, let the file update, then paste it again (weird solution, but works)

Step 6: Import results on Admeenly

Time to match the study to the bilan carbone
Most of the time, we don’t have access to exactly the one entire year of data covered by the carbon assessment (you can check dates in the source data tab).To work around this, we need to extrapolate results:

This is done via the “Export vers plateforme tab

The CRUCIAL data to enter is the Reference expense in the S2 cell
You will find this info directly in the client’s FEC

Then the final result for the carbon assessment in “Extrapolated emissions” shown in V2 cell.

Note: For AWS, both the export data is taxes INCLUDED and the FEC data is without taxes
Note 2: if you exactly have the correct export data, simply paste the R2 value into V2 ADDING RESULTS TO THE PLATFORM Download the “Export vers plateforme” tab as csv
On admeenly, go to the client’s account, on the correct year
In the activity data tab, add a cloud module (if it doesn’t already exist). The type of module needs to be “advanced”, otherwise the graphics will not show.


Click the “Edit activities” icon (pen)


Then click import activities from a file with custom emission factors, and select your csv file
Use this mapping (most of it is done automatically):


And click Next
It’s done, the study is added to the client’s account
The only steps left are to mark the associated tasks as done, and to mark related expenses on the client’s account as double counting

Additional information

Boring information if you are interested in details of how this model works (Marie can answer your questions)
For the Compute Methodology, it’s an import range from this file: the Masterfile, a collection of data from this database file
The 3rd tab and the following ones are data gathered on the internet, linking the AWS instances to their hardware, the specs of said hardware, and the share of the hardware they use.
The 2nd tab links all of this to electricity consumptions, based on a study analysis done by Ferreol
The 1st tab is where data is put in the right format to be extracted to the Masterfile
You can add data in the Handpicked data tab for any corner case not encountered yet.
For Storage and Transfer, everything is on this template file, on the methodology tabs.
There is a tiny file to get information on the amount of cooling needed per kWh of electricity consumed.
Last is carbon intensity of electricity
This data is to update every year, and the preferred source is https://app.electricitymaps.com

Manual processing - Azure####1 ) Open the template
2 ) Delete everything but the first row in the tab Bill - Data source AZURE
3) Paste data from your client


Give the file some time to “ingest” the new data (It runs every formula on the sheet for the data you just pasted)
Check the total number of rows in the source file, and in the template (Feels dumb to say it, but it happens)

4) The next step is to feed the model (see below) to take account of unrecognised lines.

Get to the “Split usages” tab.
This is where you can add keywords to classify rows.
The idea is to split rows into a category:
Computation, Storage, Transfer, Other Services or No Impact

This is a two step process:
First, understand what this row means, and which category you would put it into, by searching the Web
Second, see if the way the info is provided (What unit is given? Is it something weird or can we trace it back to physical activities) is sufficient. If not, other services is plan B

No impact is purely for taxes / other monetary factors. If there is cloud activities, default category is other services.
The key metric is the %age of expenses which are categorized by the keywords. It is the colored cell in L2: You need to lower it under 5%, at which point it turns green
Also make sure there is no major double counting (i.e. some rows having keywords of two categories, and being counted in both). This is shown on columns M and N

5) Now that the data has been categorised, time to check every row is linked to the right data.

This is checked on the XXXX Data tabs.
Transfer Data
If there are new meter names (= information about the type of data transfer, which we use to decide it this data transfers over the internet or not), add them with their transfer type under “Data used in the model”


Storage Data
Same, but this time with the capacity of each hard drive (this info is the meter name)
This is useful, as the main information we use is the amount of data (in GB) stored over a period of time. So we need to set the capacity of hard drives in GB.
(default is 1GB for storage. check with Audirc for any questions. )
Computation Data
Same, but with the number of vCPUs, to get access to the total volume
This is useful, as the main information we use is the amount of logical cores (in vCPUs) run over a period of time. So we need to set umber of vCPUs of instances.
If there is any new meter name, add the information on columns AD and AE
Check the Results tab and other tabs of this color for any weird thing that pops out in the graphs or indicators.
A good way to do so is to check orders of magnitudes on emissions per billed amount

6) Copy the template file to the client’s folder

The analysis of the data is done, no need to work on the template anymore

7) On the new file, you need to give access to files linked to the template by import ranges

Simply go on the result tab, click on errors, and allow access

8) Time to match the study to the bilan carbone

Most of the time, we don’t have access to exactly the one entire year of data covered by the carbon assessment.
To work around this, we need to extrapolate results.
This is done via the Export vers plateforme tab
The CRUCIAL data to enter is the Reference expense in the S2 cell
You will find this info directly in the client’s FEC
Then the final result for the carbon assessment in “Extrapolated emissions” shown in V2
Note: For AZURE, both the export data and the FEC data is without taxes
Note 2: if you exactly have the correct export data, simply paste the R2 value into V2
Adding results to the platform Download the “Export vers plateforme” tab as csv
On admeenly, go to the client’s account, on the correct year
In the activity data tab, add a cloud module (if it doesn’t already exists)


Click the “Edit activities” icon (pen)


Then click import activities from a file, and select your csv file
Use this mapping (most of it is done automatically):


And click Next
It’s done, the study is added to the client’s account
The only steps left are to mark the associated tasks as done, and to mark related expenses on the client’s account as double counting

Manual processing - GCP####Retrieving GCP invoices

For your first attempt at this, please contact Audric Balssa
Prerequisites:
Install the gcloud CLI
Ask Audric to add you to the project “greenly-data-warehouse” with the required IAM policies.
Install Python 3 and set up a*Virtual Environment (venv*) **.
Install necessary Python libraries: pip install google-cloud-storage google-cloud-bigquery.
Ensure Google Cloud SDK (gcloud, bq, gsutil) is installed and accessible via the script's specified paths.
You will need the following scripts to retrieve the client data. Save them locally in the same folder:
**Script 1 (GCP_KeyImport.py) **

import json
import subprocess
import os
import shlexdef extract_gcp_info(file_path):
"""Extracts client_email and project_id from a Google Cloud JSON key file.""" try:
 with open(file_path,'r') as f:
 key_data = json.load(f)
 return key_data.get('client_email'), key_data.get('project_id')
 except Exception as e:
 print(f"Error reading JSON key file: {e}")
 return None, Nonedef run_command(command, check=True):
"""Helper function to run a command and print output.""" print(f"Running: {''.join(shlex.quote(arg) for arg in command)}")
 result = subprocess.run(command, capture_output=True, text=True)
 print(result.stdout)
 print(result.stderr)
 if check and result.returncode!= 0:
 print("Command failed. Aborting.")
 return False
 return resultdef select_and_save_details():
"""Prompts for details, saves selection, and initiates the next script.""" json_key_path_raw = input("Please enter the path to your JSON key file:").strip()
 paths = shlex.split(json_key_path_raw)
 json_key_path = os.path.expanduser(paths[0])
 
 if not os.path.exists(json_key_path):
 print(f"Error: The file'{json_key_path}' does not exist.")
 return False client_email, project_id = extract_gcp_info(json_key_path)
 if not client_email or not project_id:
 print("Failed to extract client_email or project_id.")
 return False
 # Authenticate gcloud_path = "gcloud" # Use system-installed gcloud (resolved via PATH)
 command1 = [gcloud_path, "auth", "activate-service-account", client_email, f"--key-file={json_key_path}"]
 if not run_command(command1):
 return False
 print("Service account activated successfully.")# List datasets command2 = [gcloud_path, "alpha", "bq", "datasets", "list", f"--project={project_id}"]
 result2 = run_command(command2)
 if not result2: return False# Parse only the dataset ID (first column) datasets = []
 for line in result2.stdout.splitlines():
 if line.strip() and not line.startswith("ID"):
 dataset_id_full = line.strip().split()[0]# If the dataset ID contains a colon, split and take the part after the colon if':' in dataset_id_full:
 dataset_name = dataset_id_full.split(':', 1)[1]
 else:
 dataset_name = dataset_id_full
 datasets.append(dataset_name) if not datasets:
 print("No datasets found.")
 return False print("Available datasets:")
 for idx, ds in enumerate(datasets): print(f"{idx+1}: {ds}")# Robust dataset selection loop selected_dataset = None
 while selected_dataset is None:
 ds_choice = input("Select a dataset by number:")
 if ds_choice.isdigit() and 1 <= int(ds_choice) <= len(datasets):
 selected_dataset = datasets[int(ds_choice)-1]
 else:
 print("Invalid selection. Try again.")# List tables command3 = [gcloud_path, "alpha", "bq", "tables", "list", f"--project={project_id}", f"--dataset={selected_dataset}"]
 result3 = run_command(command3)
 if not result3: return False
 tables = []
 for line in result3.stdout.splitlines():
 if line.strip() and not line.startswith("DATASET_ID"):
 columns = line.strip().split()
 if len(columns) > 1:
 table_id = columns[1]
 tables.append(table_id)
 if not tables:
 print(f"No tables found in'{selected_dataset}'.")
 return False
 
 print("Available tables:")
 for idx, tb in enumerate(tables): print(f"{idx+1}: {tb}")# Robust table selection loop selected_table = None
 while selected_table is None:
 tb_choice = input("Select a table by number:")
 if tb_choice.isdigit() and 1 <= int(tb_choice) <= len(tables):
 selected_table = tables[int(tb_choice)-1]
 else:
 print("Invalid selection. Try again.")# Save selection to a file in the same folder as the key key_dir = os.path.dirname(json_key_path)
 selection_file_path = os.path.join(key_dir, "selection.json")
 with open(selection_file_path, "w") as f:
 json.dump({
"client_email": client_email,
"project_id": project_id,
"dataset_id": selected_dataset,
"table_id": selected_table
 }, f)
 print(f"Selection saved to'{selection_file_path}'.")# Start the second script and pass the path to the selection file's directory script2_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "GCP_Extract_Load.py")
 if os.path.exists(script2_path):
 print("Starting the extraction script...")
 subprocess.run(["python", script2_path, key_dir])
 else:
 print(f"Error: Could not find'{script2_path}'.")if __name__ == "__main__":
 select_and_save_details()

**Script 2 (GCP_Extract_Load.py) **

import json
import subprocess
import os
import shlex
import sysdef run_command(command, check=True):
"""Helper function to run a command and print output.""" print(f"Running: {''.join(shlex.quote(arg) for arg in command)}")
 result = subprocess.run(command, capture_output=True, text=True)
 print(result.stdout)
 print(result.stderr)
 if check and result.returncode!= 0:
 print("Command failed. Aborting.")
 return False
 return Truedef create_table_from_avro(bq_path, project_id, dataset_id, table_id, bucket_name, year, clientname):
"""Creates a BigQuery table from Avro files in GCS."""# This path must match the one used for the extract command avro_path = f"gs://{bucket_name}/{clientname}_billing-data_{year}*.avro" load_command = [
 bq_path,
"--project_id", project_id,
"load",
"--source_format=AVRO",
 f"{dataset_id}.{table_id}",
 avro_path
 ]
 print("Creating BigQuery table from Avro files...")
 return run_command(load_command)def export_query_to_csv(bq_path, project_id, dataset_id, table_id, year, clientname):
"""Exports a BigQuery query result to a local CSV file."""# Prompt for time period print("
--- Data Export ---")
 use_year = input(f"Do you want to filter by year'{year}' only? (y/n):").strip().lower()
 if use_year == "y":
 month = None
 else:
 month = input("Enter the month (MM) you want to filter by (or leave blank for all months):").strip()# Build WHERE clause if month:
 where_clause = f"WHERE invoice.month ='{year}{month}'" else:
 where_clause = f"WHERE invoice.month LIKE'{year}%'"# Build SQL query table_ref = f"{project_id}.{dataset_id}.{table_id}" sql_query = f"""SELECT
 billing_account_id as billing_account,
 service.description as service_description,
 sku.id as sku_id,
 sku.description as sku_description,
 location.country as country,
 location.region as region,
 SUM(cost) as total_cost,
 currency as billing_currency,
 SUM(usage.amount) as usage_amount,
 usage.unit as unit,
 SUM(usage.amount_in_pricing_units) as usage_inprincingunits,
 usage.pricing_unit as princing_unit,
 invoice.month as month,
 cost_type as cost_type
 FROM `{table_ref}`
 {where_clause}
 GROUP BY
 sku.id, invoice.month, billing_account_id, service.description, sku.description,
 location.location, location.country, location.region, currency, usage.unit,
 usage.pricing_unit, cost_type
""" print("
--- SQL Query ---")
 print(sql_query)# Export query results to CSV output_csv_path = os.path.join(os.getcwd(), f"{clientname}_billing_data_{year}.csv")
 query_command = [
 bq_path,
"--project_id", project_id,
"query",
"--nouse_legacy_sql",
"--format=csv",
"--max_rows=1000000", # Override default 100-row limit
 sql_query
 ]
 print(f"Exporting query results to CSV: {output_csv_path}")
 with open(output_csv_path, "w") as out_csv:
 subprocess.run(query_command, stdout=out_csv)
 print(f"CSV export complete: {output_csv_path}")def extract_billing_data(output_dir):
"""Reads selection and extracts data to GCS, creates table, and exports query results.""" selection_file_path = os.path.join(output_dir, "selection.json") if not os.path.exists(selection_file_path):
 print(f"Error:'{selection_file_path}' not found. Please run the first script again.")
 return with open(selection_file_path, "r") as f:
 selection = json.load(f)# Use the fixed project_id for job execution job_project_id = "greenly-customers-warehouse"# The source project ID comes from the selection file. source_project_id = selection["project_id"]
 # Corrected dataset IDs.  source_dataset_id = selection["dataset_id"]
 destination_dataset_id = "gcp_billing_data" source_table_id = selection["table_id"]
 
 service_account_email = selection.get("client_email", "< service-account-email>")
 
 clientname = input("Enter the client name:").strip()
 year = input("Enter the year:").strip()
 
 bucket_name = f"{clientname}_billing-data_{year}" gcs_path = f"gs://{bucket_name}/{clientname}_billing-data_{year}*.avro"
 # Set up paths to gsutil and bq script_dir = os.path.dirname(os.path.abspath(__file__))
 gsutil_path = "gsutil" # Use system-installed gsutil (resolved via PATH)
 bq_path = "bq" # Use system-installed bq (resolved via PATH)# --- ACTION REQUIRED --- print("
--- ACTION REQUIRED ---")
 print("Because of a Google Cloud organizational policy, this script cannot create the bucket automatically.")
 print("Please manually create the following bucket in the Google Cloud Console before proceeding:")
 print(f"1. Bucket Name: {bucket_name}")
 print("2. Location: As specified by your organization's policy (e.g., EU, us-central1).")
 print("3. Ensure the service account has'Storage Admin' permissions on this bucket.")
 input("Press Enter to continue after the bucket has been created manually...")# Run the extract command (Avro export) source_table = f"{source_project_id}:{source_dataset_id}.{source_table_id}" extract_command = [
 bq_path,
"extract",
 f"--project_id={job_project_id}",
"--destination_format=AVRO",
 source_table,
 gcs_path
 ]
 
 print("Starting data extraction...")
 if run_command(extract_command):
 print(f"Successfully extracted data to'{gcs_path}'.")
 else:
 print("Data extraction failed. Check command and permissions.")
 return# Create BigQuery table from Avro files in GCS print("
--- Creating BigQuery table from Avro files in GCS ---")
 new_table_id = input("Enter the name for the new BigQuery table to create from Avro files:").strip()
 if create_table_from_avro(bq_path, job_project_id, destination_dataset_id, new_table_id, bucket_name, year, clientname):
 print(f"Table'{destination_dataset_id}.{new_table_id}' created successfully from Avro files.")# Export query results to CSV export_query_to_csv(bq_path, job_project_id, destination_dataset_id, new_table_id, year, clientname)
 else:
 print("Table creation from Avro files failed.")if __name__ == "__main__":
 if len(sys.argv) > 1:
 extract_billing_data(sys.argv[1])
 else:
 print("Error: Output directory not provided. Please run from the first script.")

1. Connect to the service account set up by the client and extract billing data

Your client must have followed this tutorial. This includes creating a service account with sufficient permissions for people at Greenly to access their billing data.

In order to give you access to this service account, your client should have sent you a json key
This phase covers the execution of the two Python scripts located in the ./gcp-billing_extract-load/ directory.
A. Authentication and Selection (GCP_KeyImport.py)
Goal: Authenticate the service account and identify the source data.

  1. Execute Script 1: Run the authentication script from the terminal: Bash
    python gcp-billing_extract-load/GCP_KeyImport.py

  2. Input Credentials: Provide the full file path to the client's service account JSON key.

  3. Interactive Selection: Follow the prompts to select the target Dataset ID and Table ID containing the billing data.

  4. Output: The script saves the selection (project_id, dataset_id, table_id) to a selection.json file and automatically launches the second script.
    B. Transfer, Load, and Analyze (GCP_Extract_Load.py)
    Goal: Move the data, create the final table, and export the analysis.

  5. Manual Intervention (Required): Due to organizational policy, manually create the GCS staging bucket in the Google Cloud Console using the required naming convention and located in europe-west9 (Paris) if your client is European. You will also have to authorize the service account to save the table by either given it an Editor or Storage Admin role in the Identity and Access Management page.

  6. Input Client Details: When prompted by the script, enter the Client Name and Year.

  7. Data Extraction (bq extract): The script pulls data from the client's BigQuery table into the manually created GCS bucket (as AVRO files).

  8. Data Loading (bq load): The script creates the final table in our data warehouse's gcp_billing_data dataset by loading the AVRO files from GCS.

  9. Data Analysis & Export: The script executes the predefined SQL query to filter the data, and exports the final results to a local .csv file.

  10. Note: This last step is still prone to fail, further refinement of the script is necessary. For now you can can just query the table and download the results as a csv file.
    Sample query:

SELECT
 billing_account_id as billing_account,
 service.description as service_description,
 sku.id as sku_id,
 sku.description as sku_description,
 location.country as country,
 location.region as region,
 SUM(cost) as total_cost,
 currency as billing_currency,
 SUM(usage.amount) as usage_amount,
 usage.unit as unit,
 SUM(usage.amount_in_pricing_units) as usage_inprincingunits,
 usage.pricing_unit as princing_unit,
 invoice.month as month,
 cost_type as cost_type
 FROM `{table_ref}`
 {where_clause}
 GROUP BY
 sku.id, invoice.month, billing_account_id, service.description, sku.description,
 location.location, location.country, location.region, currency, usage.unit,
 usage.pricing_unit, cost_type

2. Integration of client data in the analysis file

A. Open the template
Open the GCP cloud analysis template by clicking here

B. Delete everything but the first row in the tab Bill - Data source GCP up to column N
C. Paste data from your client
Give the file some time to “ingest” the new data (It runs every formula on the sheet for the data you just pasted)
Check the total number of rows in the source file, and in the template (Feels dumb to say it, but it happens)
D. The next step is to feed the model (see below) to take account of unrecognised lines.
Get to the “Split usages” tab.
This is where you can add keywords to classify rows.
The key metric is the colored cell in L2: You need to lower it under 5%, at which point it turns green
Also make sure there is no major double counting (i.e. some rows having keywords of two categories, and being counted in both). This is shown on columns M and N
E. Now that the data has been categorized, time to check every row is linked to the right data.
This is checked on the XXXX Data tabs:

  • Computation Data
    A good way to do so is to check orders of magnitudes on emissions per billed amount
    Check the Results tab and other tabs of this color for any weird thing that pops out in the graphs or indicators.
    A good way to do so is again to check orders of magnitudes on emissions per billed amount
    F. Copy the template file to the client’s folder
    The analysis of the data is done, no need to work on the template anymore
    G. On the new file, you need to give access to files linked to the template by import ranges
    Simply go on the result tab, click on errors, and allow access
    H. Time to match the study to the bilan carbone
    If we don’t have access to exactly the one entire year of data covered by the carbon assessment.
    To work around this, we need to extrapolate results.
    This is done via the Export vers plateforme tab
    The CRUCIAL data to enter is the Reference expense in the S2 cell. You’ll usually enter the same amount than in the cell R2 since you have the data for the full year. Otherwise, you can use a formula like “=R2 * 8/12” in the case you only have 8 months of data.
    Then the final result for the carbon assessment in “Extrapolated emissions” shown in V2
    Note: For AZURE, both the export data and the FEC data is without taxes
    Note 2: if you exactly have the correct export data, simply paste the R2 value into S2
    ADDING RESULTS TO THE PLATFORM
    Download the “Export vers plateforme” tab as csv
    On admeenly, go to the client’s account, on the correct year
    In the activity data tab, add a cloud module (if it doesn’t already exists)


    Click the “Edit activities” icon (pen)


    Then click import activities from a file, and select your csv file
    Use this mapping (most of it is done automatically):


    And click Next
    It’s done, the study is added to the client’s account
    The only steps left are to mark the associated tasks as done, and to mark related expenses on the client’s account as double counting

Rebaseline

Rebaseline can be useful sometimes, since lcoud models are often evolving.
For E2E modules, you’ll need to make a product request to re-run the modules
For advanced modules, what you’ll need to do is copy/paste the raw data into the latest version of the template.

For AWS using client’s carbon footprint report, follow this process.

How to use the GEM to help for GCP data extraction when using a JSON file

Overview An expert assistant for Greenly’s Carbon Audit team. It automates the 5-step"Transfer then Query" procedure to extract and map client cloud billing data (GCP) into our standardized audit templates. It is designed to navigate cross-project permissions and optimize massive datasets for footprinting.

The "Golden Prompt" Template

To initiate a high-precision extraction, copy and paste this template into the GEM:

"I need a new extraction for [Client Name (anonymous)].Source Table:[ProjectID]:[Dataset].[TableID]Key File:[KeyName].jsonPeriod: [Month/Year]"

What this GEM Does-Identity Handshake: Translates your local .json keys into active Google Cloud CLI sessions.

  • The Search Party: Hunts for renamed or "hidden" billing datasets in client projects when IDs are outdated.

  • The Staging Lift: Manages the bq cp transfer from client projects to the Greenly Staging warehouse.

  • Shortcut SQL: Generates optimized queries that pull directly from Staging to bypass folder-level permission locks.

  • Data Slimming: Automatically applies GROUP BY and DATE_TRUNC logic to reduce row counts for high-volume clients.

When to Contact David MacGladrie

The GEM can solve logic and syntax issues, but it cannot grant itself permissions. Use this guide to know when to ping David:

  • "Job Creation Denied": The Service Account can't "work" in the Greenly project.

  • Ask David: "Grant BigQuery Job User to the Service Account at the Project level."

  • "Table Create/Update Denied": The Service Account can't write data to the staging area.

  • Ask David: "Grant BigQuery Data Editor to the Service Account on the **gcp_billing_data_stagingdataset."

  • "External Domain Block": David sees a red error adding an account.

  • Ask David: "This is an external domain; please check the Domain Restrict Sharing organization policy."



Examples of common edge cases:

  1. The "Identity Paradox" (Mediarithmics)

    • The Issue: Authenticating as a client service account to read their data, but needing to be myself to write to the Greenly project.

    • The Pain Point: I can't be two "identities" at once in a single terminal command. I have to perform an "Identity Dance"—switching gcloud auth back and forth just to bridge the gap between the client's source and my destination.

  1. Regional Data Silos (Mediarithmics / Equativ)

    • The Issue: BigQuery cannot "see" across regions in a single query.

    • The Pain Point: If Mediarithmics data is in the EU (multi-region) and Greenly is in europe-west9 (Paris), a direct INSERT fails. This forces me to create a "Landing Zone" (Staging dataset) in the same region as the client before I can move it to our production dataset.

  1. The "Silent Table" Permission (Equativ)

    • The Issue: Having BigQuery Data Viewer at the dataset level but not the table level (or vice versa).

    • The Pain Point: In the Equativ case, the client granted access to the dataset, but the specific billing table was likely restricted by a more granular permission set. I see the folder, but the files inside are invisible to me.

  1. Quota & Billing Project Confusion (Mediarithmics)

    • The Issue: BigQuery needs to know which project to "bill" for the compute power of a data move.

    • The Pain Point: Even when I have the right identity, the terminal often stays stuck trying to charge the client's project for the work, triggering a jobs.create error. I have to manually force the billing/quota_project back to Greenly to execute the job.

  1. View vs. Table Extraction (OFA / General)

    • The Issue: Clients often provide a BigQuery View instead of a raw Table.
      The Pain Point: I cannot simply "copy" a View. To get the data into my warehouse, I have to run a full SELECT * and save the results, which is more expensive and prone to timeout errors than a standard table copy.

4. Data Integration

👉 With the E2E module, data integration on the client’s account will be automatic → No need to read further
👉 In order to integrate data on the platform, you must go through the OneSchema process on the module from Admeely
💡For any information on Admeenly’s “Activity data” page or OneSchema, don’t hesitate to check the below page: “Add modules and begin your physical analysis: Activity Data 💡Important specifics for data format in your export tab

  • Negative quantities will generate errors from OneSchema, make sure you don’t have any in your file

  • The algorithm does not accept numer format with thousands separators (ex: 1, 563, 732.45, or 1 563 732, 45) → To make sure that the format is correct:

  • Remove any space as thousands separators (otherwise the format will be considered as text)

  • If you see any “,” as a decimal separator, convert them as points

  • Select your whole column > “123” in the control panel > Select “Automatic” (you should get 1563732.45 for the same example)

⚠️ Once you have followed OneSchema’s integration process and the algorithm is calculating emissions, check the status of your file at the bottom of the module:

  • “Processing”: calculations from the algorithm are still ongoing

  • “Completed”: calculations are finalized → Emissions and visuals on the module are final, and you can set the module as “Done” on the Mission Control

  • “Processing error”: click on “See import logs” to check the source of the issue

  • It’s often due to an emission factor id that is no longer existing (database updates can happen) → You can check by yourself or with the R& M team what the correct id is

  • For any source that you can’t manage yourself (other than data format), check with the product team by creating a ticket

  • Once you have corrected the errors, you can go through the import process again until you the “Completed” status

Did this answer your question?