#!/bin/bash
# Copyright 2024 Google Inc. 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.


# =======================
# WHAT THIS SCRIPT IS FOR
# =======================
# Given a project ID, this script completes the setup steps that are necessary
# for running the `gcloud compute instances ops-agents policies` commands.
# See the `WHAT THIS SCRIPT DOES` section for more details.

# ==========================
# WHO SHOULD RUN THIS SCRIPT
# ==========================
# This script is designed for project OWNERS to run. Non-owner users may be able
# to run it if they have highly privileged roles. Reach out to your project
# owner for help if certain permission (e.g. enabling API for the entire
# project) is denied.

# =====================
# WHAT THIS SCRIPT DOES
# =====================
# Given a project ID, this script takes care of the following setup:
#
# 1. Enable the Cloud Logging API, the Cloud Monitoring API and the OS Config
#    API for the project. This is required for logs and metrics to be ingested
#    to the backend.
#    1.1 If the OS Config API was not yet enabled, enable it in limited
#        functionality mode. The VM Manager limited functionality mode is free
#        functionality that is only sufficient for applying ops agents policies.
# 2. Enable and verify the OS Config metadata for the project. This ensures the
#    OS Config Agent installed on each Google Compute Engine VMs is active and
#    ready to pull and apply policies.
# 3. [Optional] Grant IAM roles to non-owner users or service accounts to
#    create and manage policies. Project owner(s) automatically have full access
#    to create and manage policies. For all other users or service accounts, the
#    project owner(s) need to explicit grant IAM roles to them. The available
#    options for the IAM roles are:
#    *  roles/osconfig.osPolicyAssignmentAdmin: Full access to the policies.
#    *  roles/osconfig.osPolicyAssignmentEditor: Edit access to get, update, and list policies.
#    *  roles/osconfig.osPolicyAssignmentViewer: Read-only access to get and list policies.

# ============
# Sample usage
# ============
#
# A sample command that does 1, 2 and 3 as mentioned above:
# $  bash prepare-for-ops-agents-policies.sh --project=PROJECT
#
# A sample command that does 1, 2, 3 and 4 (grant roles to a user):
# $  bash prepare-for-ops-agents-policies.sh --project=PROJECT \
#      --iam-user=USER_EMAIL \
#      --iam-policy-access=[admin or editor or viewer]
#
# A sample command that does 1, 2, 3 and 4 (grant roles to a service account):
# $  bash prepare-for-ops-agents-policies.sh --project=PROJECT \
#      --iam-service-account=SERVICE_ACCT_EMAIL \
#      --iam-policy-access=[admin or editor or viewer]

# ===============
# TROUBLESHOOTING
# ===============
#
# PERMISSION_DENIED errors
#
# If you see an error like:
#
#     WARNING: You do not appear to have access to project [PROJECT_ID] or it
#     does not exist.
#
# Make sure the project exists and you have access to it. Ask your project owner
# for help if needed.
#
# If you see an error like:
#
#     ERROR: (gcloud.services.enable) PERMISSION_DENIED: The caller does not
#     have permission.
#
# Refer to the `WHO SHOULD RUN THIS SCRIPT` section to make sure you are an
# owner of this project or ask the owner to grant you sufficient permission
# following https://cloud.google.com/iam/docs/understanding-roles.

# Ignore the return code of command substitution in variables.
# shellcheck disable=SC2155

set -e

function troubleshooting {
  echo 'If you see a "Successfully finished executing the script." message above, everything should be all set. If any step failed, check out the TROUBLESHOOTING section of this script to see how to handle the error.'
}

trap troubleshooting EXIT

show_usage(){
  case "$1" in
    0)
    echo "Usage: bash prepare-for-ops-agents-policies.sh --project=PROJECT --iam-user=USER_EMAIL --iam-policy-access=[admin or editor or viewer]."
    echo "Usage: bash prepare-for-ops-agents-policies.sh --project=PROJECT --iam-service-account=SERVICE_ACCT_EMAIL --iam-policy-access=[admin or editor or viewer]."
    echo "Usage: bash prepare-for-ops-agents-policies.sh --project=PROJECT.";;
    1)
    echo "Usage: allowable value for --iam-policy-access=[admin or editor or viewer].";;
    2)
    echo "Usage: --iam-service-account and --iam-user are mutually exclusive.";;
    3)
    echo "Usage: --iam-policy-access was defined and it must come with --iam-service-account or --iam-user.";;
  esac
}

# It should take 1 param (--project) or 3 params (--project, --iam-user/--iam-service-account, --iam-policy-access).
if [[ $# -ne 1 ]] && [[ $# -ne 3 ]]; then
  show_usage 0
  exit 1
fi

while getopts -- '-:' OPTCHAR; do
  case "${OPTCHAR}" in
    -)
      case "${OPTARG}" in
        iam-service-account=*) IAM_SERVICE_ACCOUNT="${OPTARG#*=}" ;;
        iam-user=*) IAM_USER="${OPTARG#*=}" ;;
        iam-policy-access=*) POLICY_ACCESS_ROLE="${OPTARG#*=}" ;;
        project=*) PROJECT="${OPTARG#*=}" ;;
        *) fail "Unknown option '${OPTARG}'." ;;
      esac
  esac
done

case "${POLICY_ACCESS_ROLE}" in
  "") ;;
  admin)
    IAM_ROLE=roles/osconfig.osPolicyAssignmentAdmin;;
  editor)
    IAM_ROLE=roles/osconfig.osPolicyAssignmentEditor;;
  viewer)
    IAM_ROLE=roles/osconfig.osPolicyAssignmentViewer;;
  *)
    show_usage 1; exit 1;;
esac

if [[ -n ${IAM_ROLE} ]]; then
  if [[ -z ${IAM_SERVICE_ACCOUNT} ]] && [[ -z ${IAM_USER} ]]; then
    show_usage 3
    exit 1
  fi
  if [[ -n ${IAM_SERVICE_ACCOUNT} ]] && [[ -n ${IAM_USER} ]]; then
    show_usage 2
    exit 1
  fi
fi



echo "Step 1: Enable the Cloud Logging API, the Cloud Monitoring API and the OS Config API for the project by running the following set of commands:"
gcloud config set project "$PROJECT"
gcloud services enable logging.googleapis.com
gcloud services enable monitoring.googleapis.com
gcloud services list --filter="NAME ~ osconfig.googleapis.com"
if [[ $? -eq 1 ]]; then
  gcloud services enable osconfig.googleapis.com
  gcloud compute os-config project-feature-settings update '--patch-and-config-feature-set=limited'
  echo "The OS Config API is now enabled in limited functionality mode."
  echo "In VM Manager limited functionality mode, you get a subset of features (including installing ops-agent) at no cost for unlimited number of VMs."
fi
echo "Successfully finished step 1."

echo "Step 2: Enable and verify the OS Config metadata for the project:"
gcloud compute project-info add-metadata --metadata=enable-guest-attributes=TRUE,enable-osconfig=TRUE
echo "Successfully finished step 2."

if [[ -n ${IAM_SERVICE_ACCOUNT} ]]; then
  IAM_ENTITY="serviceAccount:${IAM_SERVICE_ACCOUNT}"
elif [[ -n ${IAM_USER} ]]; then
  IAM_ENTITY="user:${IAM_USER}"
fi

if [[ -n ${IAM_ROLE} ]]; then
  echo "Step 3: Grant the corresponding Identity and Access Management (IAM) role to ${IAM_ENTITY}:"
  gcloud projects add-iam-policy-binding "${PROJECT}" \
    --role "${IAM_ROLE}" \
    --member "${IAM_ENTITY}"
  echo "Successfully finished step 3."
fi

echo "Successfully finished executing the script."
