From 3de059de8d8005e9b4bed1defb8a6cf1e9e2051f Mon Sep 17 00:00:00 2001 From: Anupam Date: Thu, 5 Mar 2026 09:29:32 +0530 Subject: [PATCH 1/6] refactor: kzb-01 fldr restructure --- python/gcp-compute/get_compute_engine_list.py | 27 +++++++ python/gcp-compute/get_gce_metadata.py | 34 +++++++++ python/gcp-compute/get_gce_properties.py | 34 +++++++++ python/gcp-compute/get_instances_with_eips.py | 25 +++++++ .../gcp-compute/get_unused_compute_disks.py | 20 +++++ .../gcp-compute/get_unused_external_ips_1.py | 26 +++++++ .../gcp-compute/get_unused_external_ips_2.py | 74 +++++++++++++++++++ python/gcp-compute/requirements.txt | 3 + python/gcp-compute/stop_compute_instance.py | 44 +++++++++++ .../gcp-resource-manager/get_all_projects.py | 40 ++++++++++ python/gcp-resource-manager/requirements.txt | 1 + python/gcp-security/get_key_details.py | 22 ++++++ python/gcp-security/requirements.txt | 2 + 13 files changed, 352 insertions(+) create mode 100644 python/gcp-compute/get_compute_engine_list.py create mode 100644 python/gcp-compute/get_gce_metadata.py create mode 100644 python/gcp-compute/get_gce_properties.py create mode 100644 python/gcp-compute/get_instances_with_eips.py create mode 100644 python/gcp-compute/get_unused_compute_disks.py create mode 100644 python/gcp-compute/get_unused_external_ips_1.py create mode 100644 python/gcp-compute/get_unused_external_ips_2.py create mode 100644 python/gcp-compute/requirements.txt create mode 100644 python/gcp-compute/stop_compute_instance.py create mode 100644 python/gcp-resource-manager/get_all_projects.py create mode 100644 python/gcp-resource-manager/requirements.txt create mode 100644 python/gcp-security/get_key_details.py create mode 100644 python/gcp-security/requirements.txt diff --git a/python/gcp-compute/get_compute_engine_list.py b/python/gcp-compute/get_compute_engine_list.py new file mode 100644 index 0000000..ba35602 --- /dev/null +++ b/python/gcp-compute/get_compute_engine_list.py @@ -0,0 +1,27 @@ +import argparse +from google.cloud import compute_v1 + +def get_compute_instances(project_id): + """ Prints all the google compute instances of a project """ + + instance_client = compute_v1.InstancesClient() + + request = compute_v1.AggregatedListInstancesRequest() + request.project = project_id + request.max_results = 50 + + agg_list = instance_client.aggregated_list(request=request) + + for zone, response in agg_list: + if response.instances: + for instance in response.instances: + print(f"Instance Name: {instance.name}, Instance ID: {instance.id}, Instance Zone: {zone}") + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument("project_id", help="Your Google Cloud project ID.") + args = parser.parse_args() + get_compute_instances(args.project_id) + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/python/gcp-compute/get_gce_metadata.py b/python/gcp-compute/get_gce_metadata.py new file mode 100644 index 0000000..6986cdf --- /dev/null +++ b/python/gcp-compute/get_gce_metadata.py @@ -0,0 +1,34 @@ +# Import the required python libraries +import requests + +def get_instance_metadata(root_url, request_header, metadata_list): + """ + This function returns the google compute instance metadata. + + Note: You can query the contents of the metadata server by making a request to the root URLs from within + a virtual machine instance. + Use the http://metadata.google.internal/computeMetadata/v1/ root URL to make requests to metadata server. + + Ref: https://cloud.google.com/compute/docs/metadata/overview#querying + """ + + for metadata in metadata_list: + try: + response = requests.get(root_url + metadata, headers=request_header) + print(response.text) + except Exception as error: + print(f'Error occurred: {error}') + +def main(): + """ + This main function calls the `get_instance_metadata` function to get google compute instance metadata. + """ + + root_url = "http://metadata.google.internal/computeMetadata/v1/" + request_header = {"Metadata-Flavor": "Google"} + metadata_list = ["instance/hostname", "instance/network-interfaces/0/ip"] + + get_instance_metadata(root_url, request_header, metadata_list) + +if __name__ == "__main__": + main() diff --git a/python/gcp-compute/get_gce_properties.py b/python/gcp-compute/get_gce_properties.py new file mode 100644 index 0000000..1582f0f --- /dev/null +++ b/python/gcp-compute/get_gce_properties.py @@ -0,0 +1,34 @@ +# Import the required python libraries +from google.cloud import compute_v1 + +def get_instance_properties(project_id, zone, instance_name): + """ + This function returns the google compute instance properties. + """ + + instances_client = compute_v1.InstancesClient() + + try: + instance_properties = instances_client.get(project=project_id, zone=zone, instance=instance_name) + if instance_properties: + print(f"Instance Name: {instance_properties.name}") + print(f"Instance ID: {instance_properties.id}") + print(f"Instance Status: {instance_properties.status}") + print(f"Instance Machine Type: {instance_properties.machine_type}") + print(f"Instance Creation Time: {instance_properties.creation_timestamp}") + except Exception as error: + print(f"Error retrieving instance properties: {error}") + +def main(): + """ + This main function calls the `get_instance_properties` function to get google compute instance properties. + """ + + project_id = "UPDATE_ME" + zone = "UPDATE_ME" + instance_name = "UPDATE_ME" + + get_instance_properties(project_id, zone, instance_name) + +if __name__ == "__main__": + main() diff --git a/python/gcp-compute/get_instances_with_eips.py b/python/gcp-compute/get_instances_with_eips.py new file mode 100644 index 0000000..bdba7bf --- /dev/null +++ b/python/gcp-compute/get_instances_with_eips.py @@ -0,0 +1,25 @@ +import argparse +from google.cloud import compute_v1 + +def get_gce_with_eips(project_id): + """ Prints all the instances with external IP in any of its network interface """ + + instance_client = compute_v1.InstancesClient() + agg_list = instance_client.aggregated_list(project=project_id) + + for zone, response in agg_list: + if response.instances: + for instance in response.instances: + for interface in instance.network_interfaces: + if interface.access_configs: + print(f"Instance Name: {instance.name}, Instance ID: {instance.id}, Instance Zone: {zone}") + break + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument("project_id", help="Your Google Cloud project ID.") + args = parser.parse_args() + get_gce_with_eips(args.project_id) + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/python/gcp-compute/get_unused_compute_disks.py b/python/gcp-compute/get_unused_compute_disks.py new file mode 100644 index 0000000..57e8a9f --- /dev/null +++ b/python/gcp-compute/get_unused_compute_disks.py @@ -0,0 +1,20 @@ +from google.cloud import compute_v1 + +def get_unused_compute_disks(project_id): + """ + Prints the unused regional/zonal compute disks of a project. + """ + + addresses_client = compute_v1.DisksClient() + for zone, response in addresses_client.aggregated_list(project=project_id): + if response.disks: + for disk in response.disks: + if len(disk.users) == 0: + print(f"Disk Name: {disk.name}, Disk Size: {disk.size_gb}, Disk Location: {zone}") + +def main(project_id): + get_unused_compute_disks(project_id) + +if __name__ == "__main__": + project_id = "UPDATE_ME" + main(project_id) \ No newline at end of file diff --git a/python/gcp-compute/get_unused_external_ips_1.py b/python/gcp-compute/get_unused_external_ips_1.py new file mode 100644 index 0000000..91cbdcd --- /dev/null +++ b/python/gcp-compute/get_unused_external_ips_1.py @@ -0,0 +1,26 @@ +# from google.oauth2 import service_account +from google.cloud import compute_v1 + +# Use this, if authentication to be done via service account +# credentials = service_account.Credentials.from_service_account_file('sa-key.json') +# addresses_client = compute_v1.AddressesClient(credentials=credentials) + +def get_reserved_ips(project_ids): + addresses_client = compute_v1.AddressesClient() + + for project_id in project_ids: + request = compute_v1.AggregatedListAddressesRequest() + request.project = project_id + + for region, response in addresses_client.aggregated_list(request=request): + if response.addresses: + for address in response.addresses: + if(address.address_type == "EXTERNAL" and address.status == "RESERVED"): + print("External IP:", address.name, "Region:", region) + +def main(): + project_ids = ["UPDATE_ME"] + get_reserved_ips(project_ids) + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/python/gcp-compute/get_unused_external_ips_2.py b/python/gcp-compute/get_unused_external_ips_2.py new file mode 100644 index 0000000..9b81732 --- /dev/null +++ b/python/gcp-compute/get_unused_external_ips_2.py @@ -0,0 +1,74 @@ +"""Get Unsed External IP Addresses + +This script prints all the unused external IP addresses of all the projects +present under a parent(folder/organization) along with details like (region, +external IP name, project ID) + +This script requires that Cloud Client Libraries of gcp services +`google-cloud-compute` and `google-cloud-resource-manager` be installed +within the Python environment you are running this script in. + +This file can also be imported as a module and contains the following +functions: + + * get_gcp_projects - returns the list of projects under a parent + * get_reserved_ips - prints the unused eips along with details + * main - the main function of the script +""" + +from google.cloud import compute_v1, resourcemanager_v3 + +def get_gcp_projects(parent): + """Returns the list of projects under a parent (folder/org) + + Args: + parent (str): the id of the parent whose projects to be returned + + Returns: + list: a list of strings representing the projects under a parent + """ + + project_ids = [] + + # Create a client + client = resourcemanager_v3.ProjectsClient() + # Initialize request argument(s) + request = resourcemanager_v3.ListProjectsRequest(parent=parent) + # Make the request + page_result = client.list_projects(request=request) + # Handle the response + for response in page_result: + project_ids.append(response.project_id) + return project_ids + +def get_reserved_ips(project_ids): + """Prints the list of unused external IPs of a GCP Project + + Args: + project_ids (list): the list of the projects whose unused external + IP addresses to be printed + + Returns: + it returns None as the function doesn't have any return statement + but it prints the list of unused external IPs of a GCP Project + """ + + addresses_client = compute_v1.AddressesClient() + + for project_id in project_ids: + request = compute_v1.AggregatedListAddressesRequest() + request.project = project_id + + for region, response in addresses_client.aggregated_list(request=request): + if response.addresses: + for address in response.addresses: + if(address.address_type == "EXTERNAL" and address.status == "RESERVED"): + print(f"Project ID: {project_id} External IP: {address.name} Region: {region}") + +def main(): + parent="organizations/UPDATE_ME" # For folder, use "folders/UPDATE_ME" + project_ids = get_gcp_projects(parent) + get_reserved_ips(project_ids) + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/python/gcp-compute/requirements.txt b/python/gcp-compute/requirements.txt new file mode 100644 index 0000000..3fb67a8 --- /dev/null +++ b/python/gcp-compute/requirements.txt @@ -0,0 +1,3 @@ +requests +google-cloud-compute==1.11.0 +google-cloud-resource-manager==1.10.1 \ No newline at end of file diff --git a/python/gcp-compute/stop_compute_instance.py b/python/gcp-compute/stop_compute_instance.py new file mode 100644 index 0000000..682fc83 --- /dev/null +++ b/python/gcp-compute/stop_compute_instance.py @@ -0,0 +1,44 @@ +from google.cloud import compute_v1 + +def get_compute_instances(project_id): + """ + Gets the details list of targetted Google Compute Engine instances. + """ + + instance_list = [] # Initialize an empty list to store instance details + + instance_client = compute_v1.InstancesClient() + agg_list = instance_client.aggregated_list(project=project_id) + + for zone, response in agg_list: + if response.instances: + for instance in response.instances: + if ("purpose", "test") in instance.labels.items(): + temp_instance_dict = {} # Initialize a dictionary to temporary hold and dump values in a dict + temp_instance_dict.update({"instance_name" : instance.name}) + temp_instance_dict.update({"instance_zone" : zone.split("/")[1]}) + temp_instance_dict.update({"instance_project" : project_id}) + instance_list.append(temp_instance_dict) + return instance_list + +def stop_compute_instances(instance_list): + """ + Stops running Google Compute Engine instances. + """ + instance_client = compute_v1.InstancesClient() + + for instance in instance_list: + operation = instance_client.stop( + project=instance["instance_project"], zone=instance["instance_zone"], instance=instance["instance_name"] + ) + result = operation.result(timeout=300) + + print("Stop operation completed for targetted compute instances.") + +def main(project_id): + instance_list = get_compute_instances(project_id) + stop_compute_instances(instance_list) + +if __name__ == "__main__": + project_id = "UPDATE_ME" + main(project_id) \ No newline at end of file diff --git a/python/gcp-resource-manager/get_all_projects.py b/python/gcp-resource-manager/get_all_projects.py new file mode 100644 index 0000000..369f00a --- /dev/null +++ b/python/gcp-resource-manager/get_all_projects.py @@ -0,0 +1,40 @@ +from google.cloud import resourcemanager_v3 + +def list_containers(parent): + all_project_ids = [] + all_folder_ids = [] + folder_ids = [] + + prj_client = resourcemanager_v3.ProjectsClient() + prj_result = prj_client.list_projects(parent=parent) + if prj_result: + for prj_response in prj_result: + all_project_ids.append(prj_response.project_id) + + fldr_client = resourcemanager_v3.FoldersClient() + fldr_result = fldr_client.list_folders(parent=parent) + if fldr_result: + for fldr_response in fldr_result: + all_folder_ids.append(fldr_response.name) + folder_ids.append(fldr_response.name) + + while all_folder_ids: + folder_id = all_folder_ids.pop() + subfldr_result = fldr_client.list_folders(parent=folder_id) + if subfldr_result: + for subfldr_response in subfldr_result: + all_folder_ids.append(subfldr_response.name) + folder_ids.append(subfldr_response.name) + + project_under_folder_result = prj_client.list_projects(parent=folder_id) + if project_under_folder_result: + for prj_under_fldr_response in project_under_folder_result: + all_project_ids.append(prj_under_fldr_response.project_id) + return(f"Project IDs:{all_project_ids}, Folder IDs:{folder_ids}") + +def main(): + parent="organizations/UPDATE_ME" + print(list_containers(parent)) + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/python/gcp-resource-manager/requirements.txt b/python/gcp-resource-manager/requirements.txt new file mode 100644 index 0000000..0814a65 --- /dev/null +++ b/python/gcp-resource-manager/requirements.txt @@ -0,0 +1 @@ +google-cloud-resource-manager==1.10.1 \ No newline at end of file diff --git a/python/gcp-security/get_key_details.py b/python/gcp-security/get_key_details.py new file mode 100644 index 0000000..ac95f0e --- /dev/null +++ b/python/gcp-security/get_key_details.py @@ -0,0 +1,22 @@ +from google.cloud import kms_v1 + +def get_crypto_key(crypto_key_name): + client = kms_v1.KeyManagementServiceClient() + + # request = kms_v1.GetCryptoKeyRequest( + # name=crypto_key_name, + # ) + # response = client.get_crypto_key(request=request) + + response = client.get_crypto_key(name=crypto_key_name) + if response: + if(response.rotation_period.days >= 90): + print("Key rotation period is good.") + +def main(): + crypto_key_name = "projects/UPDATE_ME/locations/UPDATE_ME/keyRings/UPDATE_ME/cryptoKeys/UPDATE_ME" + get_crypto_key(crypto_key_name) + +# Executing as standalone script +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/python/gcp-security/requirements.txt b/python/gcp-security/requirements.txt new file mode 100644 index 0000000..d330145 --- /dev/null +++ b/python/gcp-security/requirements.txt @@ -0,0 +1,2 @@ +google-cloud-resource-manager==1.10.1 +google-cloud-kms==2.17.0 \ No newline at end of file From 42cb382c2420e550a8c27218890939ccabd7e945 Mon Sep 17 00:00:00 2001 From: Anupam Date: Thu, 5 Mar 2026 09:31:26 +0530 Subject: [PATCH 2/6] feat: kzb-01 add common workflows --- .github/CODEOWNERS | 5 ++++ .github/conventional-commit-lint.yaml | 5 ++++ .github/pull-request_template.md | 15 ++++++++++ .github/workflows/pull-request.yml | 40 +++++++++++++++++++++++++++ 4 files changed, 65 insertions(+) create mode 100644 .github/CODEOWNERS create mode 100644 .github/conventional-commit-lint.yaml create mode 100644 .github/pull-request_template.md create mode 100644 .github/workflows/pull-request.yml diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..5392490 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,5 @@ +# This is a comment. Each line is a file pattern followed by one or more owners. + +# These owners will be the default owners for everything in the repo. @oneanupam will be requested for review when someone opens a pull request. + +- @oneanupam diff --git a/.github/conventional-commit-lint.yaml b/.github/conventional-commit-lint.yaml new file mode 100644 index 0000000..ad67fc6 --- /dev/null +++ b/.github/conventional-commit-lint.yaml @@ -0,0 +1,5 @@ +# Config for GitHub App that ensures that commit messages and pull requests are based on conventionalcommits.org +# https://github.com/googleapis/repo-automation-bots/tree/main/packages/conventional-commit-lint + +enabled: true +always_check_pr_title: true \ No newline at end of file diff --git a/.github/pull-request_template.md b/.github/pull-request_template.md new file mode 100644 index 0000000..4bdd312 --- /dev/null +++ b/.github/pull-request_template.md @@ -0,0 +1,15 @@ +# Description +Please include a short summary of the update made along with the context. + +## Checklist +- [ ] Self-review of the code is performed. +- [ ] Code has been fully tested and completely functional. + +## Type of Change +- [ ] break: Resolved a breaking change. This will increment the major version. +- [ ] feat: A new feature or enhancement added to the codebase. This will increment the minor version. +- [ ] fix: A bug fix or correction to resolve an issue. This will increment the patch version. +- [ ] chore: Other changes not directly affecting the code (e.g., documentation update). No version increment. + +## Reference +https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/creating-a-pull-request-template-for-your-repository \ No newline at end of file diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml new file mode 100644 index 0000000..6ee7457 --- /dev/null +++ b/.github/workflows/pull-request.yml @@ -0,0 +1,40 @@ +name: Pull Request Checks + +on: + pull_request: + branches: + - master + types: + - opened + - synchronize + - reopened + +jobs: + pull-request-check: + runs-on: ubuntu-latest + steps: + # Checkout the repository code + - name: Code checkout + id: code_checkout + uses: actions/checkout@v4 + + # Check PR title prefix to ensure it follows the convention + - name: Check PR title prefix + run: | + echo "PR Title: '${{ github.event.pull_request.title }}'" + + if [[ ! "${{ github.event.pull_request.title }}" =~ ^(ci|feat|fix|chore|docs|refactor): ]]; then + echo "❌ PR title must start with one of: ci:, feat:, fix:, chore:, docs:, refactor:" + exit 1 + else + echo "✅ PR title is valid." + fi + + # Scan the repo for any sensitive information like secrets etc + - name: Secret Scanning + uses: trufflesecurity/trufflehog@main + with: + path: ./ # Code repository path + base: "" # Start scanning from here + head: ${{ github.head_ref || github.ref_name }} # Scan commits until here + extra_args: --only-verified From c6c8f7b8b8b4482f865f689b313e54eeb0cad4a0 Mon Sep 17 00:00:00 2001 From: Anupam Date: Thu, 5 Mar 2026 09:32:36 +0530 Subject: [PATCH 3/6] chore: kzb-01 add configs --- .editorconfig | 50 +++++++++++++++++++++++++++++++++++++++++ .pre-commit-config.yaml | 17 ++++++++++++++ .vscode/extensions.json | 5 +++++ .vscode/settings.json | 5 +++++ 4 files changed, 77 insertions(+) create mode 100644 .editorconfig create mode 100644 .pre-commit-config.yaml create mode 100644 .vscode/extensions.json create mode 100644 .vscode/settings.json diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..b097c31 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,50 @@ +# EditorConfig is awesome: http://EditorConfig.org + +# top-most EditorConfig file +root = true + +[*] +# Unix-style newlines at the bottom of every file +end_of_line = lf +charset = utf-8 + +# indentation style and size +indent_style = space +indent_size = 2 + +# Make sure every file has a blank line at the end +insert_final_newline = true + +# Remove any whitespace characters preceding newline characters +trim_trailing_whitespace = true + +# Give operators breathing room, but not brackets +spaces_around_operators = true +spaces_around_brackets = false + +# 4 space indentation +[*.{py,java}] +indent_size = 4 + +[*.json] +indent_size = 4 + +# 2 space indentation +[*.{js,html}] +indent_size = 2 + +[*.{tf}] +indent_size = 2 + +[*.rb] +indent_size = 2 + +[*.yml] +indent_size = 2 + +[*.yaml] +indent_size = 2 + +[*.{md}] +indent_size = unset +trim_trailing_whitespace = false diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..e3e1d37 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,17 @@ +# See https://pre-commit.com for more information +# See https://pre-commit.com/hooks.html for more hooks +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.6.0 + hooks: + - id: no-commit-to-branch + args: [--branch, main, --branch, master] + - id: trailing-whitespace + - id: end-of-file-fixer + - id: check-yaml + - id: check-added-large-files + - id: detect-private-key + - repo: https://github.com/gitleaks/gitleaks + rev: v8.18.4 + hooks: + - id: gitleaks diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..ae7c2f6 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,5 @@ +{ + "recommendations": [ + "EditorConfig.EditorConfig" + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..7b6db2d --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "files.insertFinalNewline": true, + "files.trimTrailingWhitespace": true, + "files.trimFinalNewlines": true +} From fbc607417d1a835557260a539db034dbe06d46f5 Mon Sep 17 00:00:00 2001 From: Anupam Date: Thu, 5 Mar 2026 09:33:49 +0530 Subject: [PATCH 4/6] docs: kzb-01 add docs --- docs/index.md | 0 docs/usecases.md | 9 +++++++++ 2 files changed, 9 insertions(+) create mode 100644 docs/index.md create mode 100644 docs/usecases.md diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..e69de29 diff --git a/docs/usecases.md b/docs/usecases.md new file mode 100644 index 0000000..25726ed --- /dev/null +++ b/docs/usecases.md @@ -0,0 +1,9 @@ +# GCP Common UseCases +This document lists the most of the common use cases for which python scripts are written. + +1. Get all the unused external IP addresses reserved in a GCP project to save operational cost. +2. [WIP] Get all the service account in a GCP project having service account keys. +3. Get all the compute engines in a google cloud project having external IP address. +4. Get all the unused compute disks in a google cloud project to save operational cost. +5. Get all the projects in a GCP organization. +6. Stop all the google compute engine instances based on a label association. \ No newline at end of file From 0b004fe22639e0e20fe7998295b29c611d6c0fa7 Mon Sep 17 00:00:00 2001 From: Anupam Date: Thu, 5 Mar 2026 09:34:46 +0530 Subject: [PATCH 5/6] docs: kzb-01 add contributing guidelines --- CONTRIBUTING.md | 59 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..441594c --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,59 @@ +# Contribution + +This document provides guidelines for contributing to the project. + +## Pull Request + +Pull requests are the best way to propose changes to the codebase (we use ["fork-and-pull" Git workflow](https://github.com/susam/gitpr)). We actively welcome your pull requests: + +1. Fork the repository to your own Github account. +2. Clone the project to your machine. +3. Create a branch locally with a succinct but descriptive name. +4. Commit changes to the branch following any formatting and testing guidelines specific to this repo. +5. Push changes to your forked repository. +6. Open a Pull Request in our repository. + +## Guidelines + +### Commit Message Guidelines + +Use the combination of "Commit Type" and "Commit Summary" with an optional "Commit description". + +- Commit Type: Use the proper commit type for the changes as per [conventional commit](https://www.conventionalcommits.org/en/v1.0.0/) types. +- Commit Summary: Always use the imperative present tense (Write your commit messages as if you're giving a command or describing what the commit does, not what you did). Don’t capitalize the first letter of the commit message. Don’t use a period at the end of your text. + +``` +Ex: Suppose, You updated a file. So, the commit message could be - + docs: update readme file + feat: add application dockerfile +``` + +### PR Guidelines + +Format: [Commit Type] Short Summary + +``` +Ex: Suppose, You added some functionality. So, the title could be - + [feat] added function to read input from user +``` + +### Coding Guidelines + +- Try to put comments in your code, where required. +- Try to follow DRY (Don't Repeat Yourself) principle. +- Follow the style guide to write terraform code recommended by terraform. + +## Report Bugs + +We use GitHub issues to track bugs. Report a bug by opening a new issue. + +## Linting and Formatting + +All of the bash scripts in the repository must be linted or formatted using `shellcheck` to maintain a standard of quality. + +- On the web, Paste a shell script on https://www.shellcheck.net for instant feedback. ShellCheck.net is always synchronized to the latest git commit, and is the easiest way to give ShellCheck a go. +- From your terminal, Run shellcheck yourscript in your terminal for instant output, + +## License + +By contributing, you agree that your contributions will be licensed under its [MIT License](LICENSE). From b798cc1a84ef8fd55f70ab702990bc62b266a3b3 Mon Sep 17 00:00:00 2001 From: Anupam Date: Thu, 5 Mar 2026 09:35:52 +0530 Subject: [PATCH 6/6] docs: kzb-01 update readme --- README.md | 25 +++++-- .../get_compute_engine_list.py | 27 ------- .../gcp-compute-engine/get_gce_metadata.py | 34 --------- .../gcp-compute-engine/get_gce_properties.py | 34 --------- .../get_instances_with_eips.py | 25 ------- .../get_unused_compute_disks.py | 20 ----- .../get_unused_external_ips_1.py | 26 ------- .../get_unused_external_ips_2.py | 74 ------------------- .../gcp-compute-engine/requirements.txt | 3 - .../stop_compute_instance.py | 44 ----------- python-samples/gcp-key-management/.gitkeep | 0 .../gcp-key-management/get_key_details.py | 22 ------ .../gcp-key-management/requirements.txt | 2 - .../gcp-resource-manager/get_all_projects.py | 40 ---------- .../gcp-resource-manager/requirements.txt | 1 - usecases.md | 9 --- 16 files changed, 19 insertions(+), 367 deletions(-) delete mode 100644 python-samples/gcp-compute-engine/get_compute_engine_list.py delete mode 100644 python-samples/gcp-compute-engine/get_gce_metadata.py delete mode 100644 python-samples/gcp-compute-engine/get_gce_properties.py delete mode 100644 python-samples/gcp-compute-engine/get_instances_with_eips.py delete mode 100644 python-samples/gcp-compute-engine/get_unused_compute_disks.py delete mode 100644 python-samples/gcp-compute-engine/get_unused_external_ips_1.py delete mode 100644 python-samples/gcp-compute-engine/get_unused_external_ips_2.py delete mode 100644 python-samples/gcp-compute-engine/requirements.txt delete mode 100644 python-samples/gcp-compute-engine/stop_compute_instance.py delete mode 100644 python-samples/gcp-key-management/.gitkeep delete mode 100644 python-samples/gcp-key-management/get_key_details.py delete mode 100644 python-samples/gcp-key-management/requirements.txt delete mode 100644 python-samples/gcp-resource-manager/get_all_projects.py delete mode 100644 python-samples/gcp-resource-manager/requirements.txt delete mode 100644 usecases.md diff --git a/README.md b/README.md index c112bca..0c4104a 100644 --- a/README.md +++ b/README.md @@ -1,20 +1,25 @@ # python-gcp-samples + This repository contains python samples intended to use in day to day operational activities on google cloud platform. Usecases for which the python scripts are written can be found [here](./usecases.md). ## Prerequisites + Below prerequisites must be fulfilled for the successful execution of code. ### Software Requirement + Resources in this repository are meant for use with Python 3.x (check the version using `python3 --version`) and pip3 (check the version using `pip3 --version`). If you don't have the compatible version, download it from official python repository. -- [python3](https://www.python.org/downloads/) >= 3.9.2 +- [python3](https://www.python.org/downloads/) >= 3.13.2 - [pip3](https://pypi.org/project/pip/) >= 20.3.4 ### Bootstrap Virtual Environment + [venv](https://docs.python.org/3/library/venv.html) is a tool that creates isolated Python environments. These isolated environments can have separate versions of Python packages, which allows you to isolate one project's dependencies from the dependencies of other projects. **Linux** + ``` cd your-project python3 -m venv env @@ -28,35 +33,41 @@ Resources in this repository are meant for use with Python 3.x (check the versio **Note:** Follow the [google article](https://cloud.google.com/python/docs/setup) to setup your Python development environment. ## Quick Start + If you want to quickly run and test Python samples without installing python, the recommended approach is to use Cloud Shell. Cloud Shell is a Compute Engine virtual machine. The service credentials associated with this virtual machine are automatic, so there is no need to set up or download a service account key. Cloud shell terminal is preloaded with softwares and utilities such as Python, gcloud command-line tool, kubectl, and more. letting you get started with less setup. + - **Step-01:** Activate Cloud Shell at the top of the Google Cloud Console. - **Step-02:** Clone this repository: `git clone https://github.com/anupam-sy/python-gcp-samples.git` - **Step-03:** Setup the python virtual environment using [Bootstrap Virtual Environment](#bootstrap-virtual-environment). ### Authentication and Authorization + This client library used in the python script supports authentication via Google Application Default Credentials, or by providing a JSON key file for a Service Account. Google Application Default Credentials (ADC) is the recommended way to authorize and authenticate clients. ## Accessing Cloud APIs + You can access Cloud APIs using client libraries available for many popular programming languages While you can use Google Cloud APIs directly by making raw requests to the server, client libraries provide simplifications that significantly reduce the amount of code you need to write. -1. Cloud Client Libraries are the recommended option for accessing Cloud APIs programmatically, where available. Cloud Client Libraries use the latest client library model. -[NEW - Recommended Way] https://github.com/googleapis/google-cloud-python +1. Cloud Client Libraries are the recommended option for accessing Cloud APIs programmatically, where available. Cloud Client Libraries use the latest client library model. + [NEW - Recommended Way] https://github.com/googleapis/google-cloud-python -2. A few Google Cloud APIs don't have Cloud Client Libraries available in all languages. If you want to use one of these APIs and there is no Cloud Client Library for your preferred language, you can still use the previous style of client library, called Google API Client Libraries. -[OLD - Not Recommended] https://github.com/googleapis/google-api-python-client +2. A few Google Cloud APIs don't have Cloud Client Libraries available in all languages. If you want to use one of these APIs and there is no Cloud Client Library for your preferred language, you can still use the previous style of client library, called Google API Client Libraries. + [OLD - Not Recommended] https://github.com/googleapis/google-api-python-client **Note:** It is recommended to use Cloud Client Libraries for Python, where possible, for new code development due to the following reasons: With Cloud Client Libraries for Python: + - There is a separate client library for each API, so you can choose which client libraries to download. Whereas, google-api-python-client is a single client library for all APIs. As a result, the total package size for google-api-python-client exceeds 50MB. - There are stricter controls for breaking changes to the underlying APIs as each client library is focused on a specific API. - There are more features in these Cloud Client Libraries as each library is focused on a specific API, and in some cases, the libraries are owned by team who specialized in that API. ## References + - https://cloud.google.com/python/docs/setup - https://cloud.google.com/apis/docs/overview - https://cloud.google.com/apis/docs/client-libraries-explained @@ -70,7 +81,9 @@ With Cloud Client Libraries for Python: - https://github.com/GoogleCloudPlatform/python-docs-samples ## License + This repository is under MIT License. ## Providing feedback -Open an issue in this GitHub repository. \ No newline at end of file + +Open an issue in this GitHub repository. diff --git a/python-samples/gcp-compute-engine/get_compute_engine_list.py b/python-samples/gcp-compute-engine/get_compute_engine_list.py deleted file mode 100644 index ba35602..0000000 --- a/python-samples/gcp-compute-engine/get_compute_engine_list.py +++ /dev/null @@ -1,27 +0,0 @@ -import argparse -from google.cloud import compute_v1 - -def get_compute_instances(project_id): - """ Prints all the google compute instances of a project """ - - instance_client = compute_v1.InstancesClient() - - request = compute_v1.AggregatedListInstancesRequest() - request.project = project_id - request.max_results = 50 - - agg_list = instance_client.aggregated_list(request=request) - - for zone, response in agg_list: - if response.instances: - for instance in response.instances: - print(f"Instance Name: {instance.name}, Instance ID: {instance.id}, Instance Zone: {zone}") - -def main(): - parser = argparse.ArgumentParser() - parser.add_argument("project_id", help="Your Google Cloud project ID.") - args = parser.parse_args() - get_compute_instances(args.project_id) - -if __name__ == "__main__": - main() \ No newline at end of file diff --git a/python-samples/gcp-compute-engine/get_gce_metadata.py b/python-samples/gcp-compute-engine/get_gce_metadata.py deleted file mode 100644 index 6986cdf..0000000 --- a/python-samples/gcp-compute-engine/get_gce_metadata.py +++ /dev/null @@ -1,34 +0,0 @@ -# Import the required python libraries -import requests - -def get_instance_metadata(root_url, request_header, metadata_list): - """ - This function returns the google compute instance metadata. - - Note: You can query the contents of the metadata server by making a request to the root URLs from within - a virtual machine instance. - Use the http://metadata.google.internal/computeMetadata/v1/ root URL to make requests to metadata server. - - Ref: https://cloud.google.com/compute/docs/metadata/overview#querying - """ - - for metadata in metadata_list: - try: - response = requests.get(root_url + metadata, headers=request_header) - print(response.text) - except Exception as error: - print(f'Error occurred: {error}') - -def main(): - """ - This main function calls the `get_instance_metadata` function to get google compute instance metadata. - """ - - root_url = "http://metadata.google.internal/computeMetadata/v1/" - request_header = {"Metadata-Flavor": "Google"} - metadata_list = ["instance/hostname", "instance/network-interfaces/0/ip"] - - get_instance_metadata(root_url, request_header, metadata_list) - -if __name__ == "__main__": - main() diff --git a/python-samples/gcp-compute-engine/get_gce_properties.py b/python-samples/gcp-compute-engine/get_gce_properties.py deleted file mode 100644 index 1582f0f..0000000 --- a/python-samples/gcp-compute-engine/get_gce_properties.py +++ /dev/null @@ -1,34 +0,0 @@ -# Import the required python libraries -from google.cloud import compute_v1 - -def get_instance_properties(project_id, zone, instance_name): - """ - This function returns the google compute instance properties. - """ - - instances_client = compute_v1.InstancesClient() - - try: - instance_properties = instances_client.get(project=project_id, zone=zone, instance=instance_name) - if instance_properties: - print(f"Instance Name: {instance_properties.name}") - print(f"Instance ID: {instance_properties.id}") - print(f"Instance Status: {instance_properties.status}") - print(f"Instance Machine Type: {instance_properties.machine_type}") - print(f"Instance Creation Time: {instance_properties.creation_timestamp}") - except Exception as error: - print(f"Error retrieving instance properties: {error}") - -def main(): - """ - This main function calls the `get_instance_properties` function to get google compute instance properties. - """ - - project_id = "UPDATE_ME" - zone = "UPDATE_ME" - instance_name = "UPDATE_ME" - - get_instance_properties(project_id, zone, instance_name) - -if __name__ == "__main__": - main() diff --git a/python-samples/gcp-compute-engine/get_instances_with_eips.py b/python-samples/gcp-compute-engine/get_instances_with_eips.py deleted file mode 100644 index bdba7bf..0000000 --- a/python-samples/gcp-compute-engine/get_instances_with_eips.py +++ /dev/null @@ -1,25 +0,0 @@ -import argparse -from google.cloud import compute_v1 - -def get_gce_with_eips(project_id): - """ Prints all the instances with external IP in any of its network interface """ - - instance_client = compute_v1.InstancesClient() - agg_list = instance_client.aggregated_list(project=project_id) - - for zone, response in agg_list: - if response.instances: - for instance in response.instances: - for interface in instance.network_interfaces: - if interface.access_configs: - print(f"Instance Name: {instance.name}, Instance ID: {instance.id}, Instance Zone: {zone}") - break - -def main(): - parser = argparse.ArgumentParser() - parser.add_argument("project_id", help="Your Google Cloud project ID.") - args = parser.parse_args() - get_gce_with_eips(args.project_id) - -if __name__ == "__main__": - main() \ No newline at end of file diff --git a/python-samples/gcp-compute-engine/get_unused_compute_disks.py b/python-samples/gcp-compute-engine/get_unused_compute_disks.py deleted file mode 100644 index 57e8a9f..0000000 --- a/python-samples/gcp-compute-engine/get_unused_compute_disks.py +++ /dev/null @@ -1,20 +0,0 @@ -from google.cloud import compute_v1 - -def get_unused_compute_disks(project_id): - """ - Prints the unused regional/zonal compute disks of a project. - """ - - addresses_client = compute_v1.DisksClient() - for zone, response in addresses_client.aggregated_list(project=project_id): - if response.disks: - for disk in response.disks: - if len(disk.users) == 0: - print(f"Disk Name: {disk.name}, Disk Size: {disk.size_gb}, Disk Location: {zone}") - -def main(project_id): - get_unused_compute_disks(project_id) - -if __name__ == "__main__": - project_id = "UPDATE_ME" - main(project_id) \ No newline at end of file diff --git a/python-samples/gcp-compute-engine/get_unused_external_ips_1.py b/python-samples/gcp-compute-engine/get_unused_external_ips_1.py deleted file mode 100644 index 91cbdcd..0000000 --- a/python-samples/gcp-compute-engine/get_unused_external_ips_1.py +++ /dev/null @@ -1,26 +0,0 @@ -# from google.oauth2 import service_account -from google.cloud import compute_v1 - -# Use this, if authentication to be done via service account -# credentials = service_account.Credentials.from_service_account_file('sa-key.json') -# addresses_client = compute_v1.AddressesClient(credentials=credentials) - -def get_reserved_ips(project_ids): - addresses_client = compute_v1.AddressesClient() - - for project_id in project_ids: - request = compute_v1.AggregatedListAddressesRequest() - request.project = project_id - - for region, response in addresses_client.aggregated_list(request=request): - if response.addresses: - for address in response.addresses: - if(address.address_type == "EXTERNAL" and address.status == "RESERVED"): - print("External IP:", address.name, "Region:", region) - -def main(): - project_ids = ["UPDATE_ME"] - get_reserved_ips(project_ids) - -if __name__ == "__main__": - main() \ No newline at end of file diff --git a/python-samples/gcp-compute-engine/get_unused_external_ips_2.py b/python-samples/gcp-compute-engine/get_unused_external_ips_2.py deleted file mode 100644 index 9b81732..0000000 --- a/python-samples/gcp-compute-engine/get_unused_external_ips_2.py +++ /dev/null @@ -1,74 +0,0 @@ -"""Get Unsed External IP Addresses - -This script prints all the unused external IP addresses of all the projects -present under a parent(folder/organization) along with details like (region, -external IP name, project ID) - -This script requires that Cloud Client Libraries of gcp services -`google-cloud-compute` and `google-cloud-resource-manager` be installed -within the Python environment you are running this script in. - -This file can also be imported as a module and contains the following -functions: - - * get_gcp_projects - returns the list of projects under a parent - * get_reserved_ips - prints the unused eips along with details - * main - the main function of the script -""" - -from google.cloud import compute_v1, resourcemanager_v3 - -def get_gcp_projects(parent): - """Returns the list of projects under a parent (folder/org) - - Args: - parent (str): the id of the parent whose projects to be returned - - Returns: - list: a list of strings representing the projects under a parent - """ - - project_ids = [] - - # Create a client - client = resourcemanager_v3.ProjectsClient() - # Initialize request argument(s) - request = resourcemanager_v3.ListProjectsRequest(parent=parent) - # Make the request - page_result = client.list_projects(request=request) - # Handle the response - for response in page_result: - project_ids.append(response.project_id) - return project_ids - -def get_reserved_ips(project_ids): - """Prints the list of unused external IPs of a GCP Project - - Args: - project_ids (list): the list of the projects whose unused external - IP addresses to be printed - - Returns: - it returns None as the function doesn't have any return statement - but it prints the list of unused external IPs of a GCP Project - """ - - addresses_client = compute_v1.AddressesClient() - - for project_id in project_ids: - request = compute_v1.AggregatedListAddressesRequest() - request.project = project_id - - for region, response in addresses_client.aggregated_list(request=request): - if response.addresses: - for address in response.addresses: - if(address.address_type == "EXTERNAL" and address.status == "RESERVED"): - print(f"Project ID: {project_id} External IP: {address.name} Region: {region}") - -def main(): - parent="organizations/UPDATE_ME" # For folder, use "folders/UPDATE_ME" - project_ids = get_gcp_projects(parent) - get_reserved_ips(project_ids) - -if __name__ == "__main__": - main() \ No newline at end of file diff --git a/python-samples/gcp-compute-engine/requirements.txt b/python-samples/gcp-compute-engine/requirements.txt deleted file mode 100644 index 3fb67a8..0000000 --- a/python-samples/gcp-compute-engine/requirements.txt +++ /dev/null @@ -1,3 +0,0 @@ -requests -google-cloud-compute==1.11.0 -google-cloud-resource-manager==1.10.1 \ No newline at end of file diff --git a/python-samples/gcp-compute-engine/stop_compute_instance.py b/python-samples/gcp-compute-engine/stop_compute_instance.py deleted file mode 100644 index 682fc83..0000000 --- a/python-samples/gcp-compute-engine/stop_compute_instance.py +++ /dev/null @@ -1,44 +0,0 @@ -from google.cloud import compute_v1 - -def get_compute_instances(project_id): - """ - Gets the details list of targetted Google Compute Engine instances. - """ - - instance_list = [] # Initialize an empty list to store instance details - - instance_client = compute_v1.InstancesClient() - agg_list = instance_client.aggregated_list(project=project_id) - - for zone, response in agg_list: - if response.instances: - for instance in response.instances: - if ("purpose", "test") in instance.labels.items(): - temp_instance_dict = {} # Initialize a dictionary to temporary hold and dump values in a dict - temp_instance_dict.update({"instance_name" : instance.name}) - temp_instance_dict.update({"instance_zone" : zone.split("/")[1]}) - temp_instance_dict.update({"instance_project" : project_id}) - instance_list.append(temp_instance_dict) - return instance_list - -def stop_compute_instances(instance_list): - """ - Stops running Google Compute Engine instances. - """ - instance_client = compute_v1.InstancesClient() - - for instance in instance_list: - operation = instance_client.stop( - project=instance["instance_project"], zone=instance["instance_zone"], instance=instance["instance_name"] - ) - result = operation.result(timeout=300) - - print("Stop operation completed for targetted compute instances.") - -def main(project_id): - instance_list = get_compute_instances(project_id) - stop_compute_instances(instance_list) - -if __name__ == "__main__": - project_id = "UPDATE_ME" - main(project_id) \ No newline at end of file diff --git a/python-samples/gcp-key-management/.gitkeep b/python-samples/gcp-key-management/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/python-samples/gcp-key-management/get_key_details.py b/python-samples/gcp-key-management/get_key_details.py deleted file mode 100644 index ac95f0e..0000000 --- a/python-samples/gcp-key-management/get_key_details.py +++ /dev/null @@ -1,22 +0,0 @@ -from google.cloud import kms_v1 - -def get_crypto_key(crypto_key_name): - client = kms_v1.KeyManagementServiceClient() - - # request = kms_v1.GetCryptoKeyRequest( - # name=crypto_key_name, - # ) - # response = client.get_crypto_key(request=request) - - response = client.get_crypto_key(name=crypto_key_name) - if response: - if(response.rotation_period.days >= 90): - print("Key rotation period is good.") - -def main(): - crypto_key_name = "projects/UPDATE_ME/locations/UPDATE_ME/keyRings/UPDATE_ME/cryptoKeys/UPDATE_ME" - get_crypto_key(crypto_key_name) - -# Executing as standalone script -if __name__ == "__main__": - main() \ No newline at end of file diff --git a/python-samples/gcp-key-management/requirements.txt b/python-samples/gcp-key-management/requirements.txt deleted file mode 100644 index d330145..0000000 --- a/python-samples/gcp-key-management/requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -google-cloud-resource-manager==1.10.1 -google-cloud-kms==2.17.0 \ No newline at end of file diff --git a/python-samples/gcp-resource-manager/get_all_projects.py b/python-samples/gcp-resource-manager/get_all_projects.py deleted file mode 100644 index 369f00a..0000000 --- a/python-samples/gcp-resource-manager/get_all_projects.py +++ /dev/null @@ -1,40 +0,0 @@ -from google.cloud import resourcemanager_v3 - -def list_containers(parent): - all_project_ids = [] - all_folder_ids = [] - folder_ids = [] - - prj_client = resourcemanager_v3.ProjectsClient() - prj_result = prj_client.list_projects(parent=parent) - if prj_result: - for prj_response in prj_result: - all_project_ids.append(prj_response.project_id) - - fldr_client = resourcemanager_v3.FoldersClient() - fldr_result = fldr_client.list_folders(parent=parent) - if fldr_result: - for fldr_response in fldr_result: - all_folder_ids.append(fldr_response.name) - folder_ids.append(fldr_response.name) - - while all_folder_ids: - folder_id = all_folder_ids.pop() - subfldr_result = fldr_client.list_folders(parent=folder_id) - if subfldr_result: - for subfldr_response in subfldr_result: - all_folder_ids.append(subfldr_response.name) - folder_ids.append(subfldr_response.name) - - project_under_folder_result = prj_client.list_projects(parent=folder_id) - if project_under_folder_result: - for prj_under_fldr_response in project_under_folder_result: - all_project_ids.append(prj_under_fldr_response.project_id) - return(f"Project IDs:{all_project_ids}, Folder IDs:{folder_ids}") - -def main(): - parent="organizations/UPDATE_ME" - print(list_containers(parent)) - -if __name__ == "__main__": - main() \ No newline at end of file diff --git a/python-samples/gcp-resource-manager/requirements.txt b/python-samples/gcp-resource-manager/requirements.txt deleted file mode 100644 index 0814a65..0000000 --- a/python-samples/gcp-resource-manager/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -google-cloud-resource-manager==1.10.1 \ No newline at end of file diff --git a/usecases.md b/usecases.md deleted file mode 100644 index 25726ed..0000000 --- a/usecases.md +++ /dev/null @@ -1,9 +0,0 @@ -# GCP Common UseCases -This document lists the most of the common use cases for which python scripts are written. - -1. Get all the unused external IP addresses reserved in a GCP project to save operational cost. -2. [WIP] Get all the service account in a GCP project having service account keys. -3. Get all the compute engines in a google cloud project having external IP address. -4. Get all the unused compute disks in a google cloud project to save operational cost. -5. Get all the projects in a GCP organization. -6. Stop all the google compute engine instances based on a label association. \ No newline at end of file