HEX
Server: Apache/2.4.65 (Ubuntu)
System: Linux ielts-store-v2 6.8.0-1036-gcp #38~22.04.1-Ubuntu SMP Thu Aug 14 01:19:18 UTC 2025 x86_64
User: root (0)
PHP: 7.2.34-54+ubuntu20.04.1+deb.sury.org+1
Disabled: pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,
Upload Files
File: //snap/google-cloud-cli/current/lib/surface/terraform/recommendations/list.py
# -*- coding: utf-8 -*- #
# Copyright 2023 Google LLC. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Command provides active assist recommendations for input Terraform plan.

Step 1: Convert Terraform plan into CAI using terraform tools.
Step 2: Fetches the recommendations using the recommender API for resources in
the CAI output.

"""

from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals

import json
import os.path

from googlecloudsdk.api_lib.recommender import insight
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.terraform import flags
from googlecloudsdk.command_lib.terraform.env_vars import EnvironmentVariables
from googlecloudsdk.core import exceptions
from googlecloudsdk.core.util import files
from surface.terraform.vet import TerraformToolsTfplanToCaiOperation


@base.ReleaseTracks(base.ReleaseTrack.ALPHA)
class List(base.ListCommand):
  """Lists recommendations relevant to the input terraform plan."""

  detailed_help = {
      'EXAMPLES':
          """
        Lists recommendations relevant to the input terraform plan.

        $ {command} tfplan.json
        """,
  }

  @staticmethod
  def Args(parser):
    """Args is called by calliope to gather arguments for this command.

    It takes arguments in alphabetical order except for no- or a clear-
    pair for that argument which can follow the argument itself.
    Args:
      parser: An argparse parser that you can use to add arguments that go
          on the command line after this command. Positional arguments are
          allowed.
    """
    flags.Terraformplanjson().AddToParser(parser)

  def Run(self, args):
    environment_variables = EnvironmentVariables()

    tfplan_to_cai_operation = TerraformToolsTfplanToCaiOperation()
    with files.TemporaryDirectory() as tempdir:
      cai_assets = os.path.join(tempdir, 'cai_assets.json')
      response = tfplan_to_cai_operation(
          command='tfplan-to-cai',
          project=environment_variables.project,
          region='',
          zone='',
          terraform_plan_json=args.terraform_plan_json,
          verbosity='debug',
          output_path=cai_assets,
          env=environment_variables.env_vars)
      self.exit_code = response.exit_code
      if self.exit_code > 0:
        # The streaming binary backed operation handles its own writing to
        # stdout and stderr, so there's nothing left to do here.
        return None
      client = insight.CreateClient(self.ReleaseTrack())

      # TODO(b/265408840): move this to util file. Create different dicts for
      # Alpha, Beta and GA release to control the insight types exposed by each.
      cai_insight_types = {
          'iam_policy': {
              'insight_type': 'google.iam.policy.Insight',
              'location': 'global',
          },
          # TODO(b/265408840): add support for more insight types.
      }

      # Read CAI and call recommender API.
      with files.FileReader(cai_assets) as f:
        try:
          cai_json = json.load(f)
        except json.JSONDecodeError:
          raise exceptions.Error("""Please check the following:
                                 - Input plan file is correct.
                                 - You have appropriate permissions to read
                                 inventory of resources inside the plan file."""
                                )
        for resource in cai_json:
          for cai_type in cai_insight_types:
            if cai_type in resource:
              if cai_insight_types[cai_type]['location'] == 'global':
                location = 'global'
              else:
                # TODO(b/265408382): fetch this location from CAI resource.
                location = 'regional'
                # TODO(b/265408382): handle case if location doesn't exists in
                # CAI (if any).
              if (
                  resource['asset_type']
                  == 'cloudresourcemanager.googleapis.com/Project'
              ):
                resource_parent = 'projects/{}'.format(
                    resource['name'].split('/')[-1]
                )
              else:
                continue
              # TODO(b/265408203): handle cases for other parent types.
              if resource_parent:
                insight_parent = '{}/locations/{}/insightTypes/{}'.format(
                    resource_parent,
                    location,
                    cai_insight_types[cai_type]['insight_type'],
                )
                # TODO(b/265408203): filter recommendations as per the specific
                # resouces under CAI instead of just the insight type filter.
                return client.List(insight_parent, args.page_size, args.limit)