Skip to content

Commit 9bdc974

Browse files
authored
CGNS | Azure onboarding script validations update (#505)
1 parent 77906f0 commit 9bdc974

1 file changed

Lines changed: 241 additions & 23 deletions

File tree

cloudguard-network-application/cgns_onboarding_azure.sh

Lines changed: 241 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ MANAGE="manage"
1414
READ_ONLY="read-only"
1515
# Roles required for onboarding
1616
ROLES=("Contributor" "User Access Administrator")
17-
17+
CLIENT_SECRET_PRINTED_ON_FIRST_RUN="Printed on the first run"
1818

1919
# Function to display usage information
2020
usage() {
@@ -24,17 +24,42 @@ usage() {
2424
echo " --subscription_id [required for '$SUBSCRIPTION_SCOPE' scope] Specifies the subscription id"
2525
echo " --management_group_id [required for '$MANAGEMENT_GROUPS_SCOPE' scope] Specifies the management group id"
2626
echo " --onboarding_mode [required] Specifies the onboard mode for CloudGuard_CGNS. Can be either 'read-only' or 'manage' [default: 'read-only']"
27-
echo " --multi_tenant_app_id [optional] Specifies CloudGuard_CGNS Azure application id - for CloudGuard_CGNS application managed"
28-
echo " --single_tenant_app_mode [optional] Specifies CloudGuard_CGNS Azure application - customer app registration handling"
27+
echo " --multi_tenant_app_id [required for CloudGuard-managed (multi-tenant) mode] Specifies CloudGuard_CGNS Azure application id - for CloudGuard_CGNS application managed"
28+
echo " --single_tenant_app_mode [required for customer-managed (single-tenant) mode] Specifies CloudGuard_CGNS Azure application - customer app registration handling"
2929
echo " --app_name [required for single tenant app mode] Specifies the name of the application to be created"
3030
echo " --dry_run [optional] Specifies whether to run the script in dry-run mode [default: 'false']"
3131
echo " --clean [optional] Specifies whether to delete all the resources that the script created [default: 'false']"
3232
echo " --quiet [optional] Specifies whether to quiet all the user interactions [default: 'false']"
3333
echo " --help Show this help message and exit"
3434
}
3535

36+
# -----------------------------------------------------------------------------
37+
# Color Output Functions
38+
#
39+
# This script defines several functions and variables to print colored text
40+
# to the terminal using ANSI escape codes. Each function prints its arguments
41+
# in a specific color and resets the color at the end.
42+
#
43+
# Variables:
44+
# end - ANSI code to reset text formatting.
45+
# red - ANSI code for red text.
46+
# yellowb - ANSI code for bold yellow text.
47+
# yellow - ANSI code for yellow text.
48+
# green - ANSI code for green text.
49+
# lightblue - ANSI code for light blue (cyan) text.
50+
#
51+
# Functions:
52+
# red - Prints arguments in red.
53+
# yellowb - Prints arguments in bold yellow.
54+
# yellow - Prints arguments in yellow.
55+
# green - Prints arguments in green.
56+
# lightblue - Prints arguments in light blue (cyan).
57+
#
58+
# Usage:
59+
# Call the desired function with the text to print in color, e.g.:
60+
# red "This is an error message"
61+
# -----------------------------------------------------------------------------
3662

37-
#Message Colors
3863
end="\033[0m"
3964
red="\033[0;31m"
4065
function red {
@@ -170,8 +195,15 @@ parse_input() {
170195
done
171196
}
172197

198+
# az_wrapper
199+
# ----------------------------
200+
# Helper function to execute Azure CLI (az) commands with optional dry-run support.
201+
# If the global variable 'dry_run' is set to "true", the function will print the command instead of executing it.
202+
# Otherwise, it runs the az command with '--only-show-errors', capturing both output and exit code.
203+
# The command's output is stored in the variable 'AzOutput', and the exit code in 'AzRetVal'.
204+
# Errors from the az command do not cause the script to exit; they are handled by the caller.
205+
# ---------------------------------------------------------------------------
173206

174-
# Wrapper function for Azure CLI commands
175207
az_wrapper() {
176208
if [ "$dry_run" = "true" ]; then
177209
echo "az $*"
@@ -260,6 +292,29 @@ rollback() {
260292
fi
261293
}
262294

295+
# rollback_delete_customer_app
296+
# ----------------------------
297+
# Deletes an Azure AD application with the specified name, including its role assignments.
298+
#
299+
# Steps performed:
300+
# 1. Lists Azure AD applications matching the given display name ($app_name).
301+
# 2. Checks for errors in listing applications.
302+
# 3. Ensures only one application matches the name; exits with error if multiple found.
303+
# 4. If no application is found, logs a message and returns.
304+
# 5. Deletes all role assignments associated with the application.
305+
# 6. Deletes the Azure AD application by its App ID.
306+
# 7. Handles errors during deletion and logs success message upon completion.
307+
#
308+
# Globals:
309+
# $app_name - The display name of the Azure AD application to delete.
310+
# $AzRetVal - Return value from the last az_wrapper command.
311+
# $AzOutput - Output from the last az_wrapper command.
312+
# $NECESSARY_PERMISSIONS_STR - String describing necessary permissions for error messages.
313+
#
314+
# Returns:
315+
# 0 if the application is not found or deleted successfully.
316+
# Exits with error if multiple applications are found or if any operation fails.
317+
# -------------------------------------------------------------------------------
263318

264319

265320
rollback_delete_customer_app() {
@@ -295,7 +350,6 @@ rollback_delete_customer_app() {
295350
}
296351

297352

298-
299353
# Function to prompt user for confirmation
300354
check_if_user_would_like_to_proceed() {
301355
local message="$1"
@@ -325,7 +379,6 @@ check_if_user_would_like_to_proceed() {
325379
}
326380

327381

328-
329382
# Function to delete multi-tenant service principal role assignments
330383
rollback_delete_multi_tenant_sp_role_assignments(){
331384
if service_principal_doesnt_exists "$multi_tenant_app_id"; then
@@ -335,8 +388,21 @@ rollback_delete_multi_tenant_sp_role_assignments(){
335388
fi
336389
}
337390

391+
# rollback_delete_role_assignments
392+
# -------------------------------------------------------------------------
393+
# Deletes all Azure role assignments for a specified application within a given scope.
394+
#
395+
# Arguments:
396+
# $1 - The principal name (application ID) whose role assignments should be deleted.
397+
#
398+
# Behavior:
399+
# - Lists all role assignments for the specified application within the onboarding scope.
400+
# - If listing fails, prints a warning message with the error details.
401+
# - Extracts the IDs of the role assignments.
402+
# - Deletes all role assignments by their IDs.
403+
# - If deletion fails, prints a warning message with the error details.
404+
# --------------------------------------------------------------------------
338405

339-
# Function to delete role assignments
340406
rollback_delete_role_assignments(){
341407
local app_to_delete=$1
342408
local role_assignments
@@ -399,7 +465,27 @@ set_scope() {
399465

400466

401467

402-
# Function to validate user permissions
468+
# validate_user_permissions
469+
#------------------------------------------------------------------------------
470+
# Validates whether the currently signed-in Azure user has sufficient permissions
471+
# to perform role assignments within a specified Azure scope.
472+
#
473+
# Arguments:
474+
# $1 - The Azure scope (e.g., subscription, resource group, or resource ID)
475+
#
476+
# Behavior:
477+
# - Retrieves the signed-in user's principal name using Azure CLI.
478+
# - Lists the role assignments for the user at the specified scope, including
479+
# inherited and group-based assignments.
480+
# - Checks if the user has permissions to assign roles by invoking
481+
# validate_user_can_assign_role.
482+
# - Outputs the result and exits with an error message if any Azure CLI
483+
# operation fails.
484+
#
485+
# Returns:
486+
# 0 if the user has sufficient permissions, otherwise exits with an error.
487+
#------------------------------------------------------------------------------
488+
403489
validate_user_permissions() {
404490
local scope_type="$1"
405491

@@ -478,24 +564,107 @@ get_app_id() {
478564
fi
479565
}
480566

567+
# check_if_app_exists
568+
# ---------------------
569+
# Validates whether an Azure AD application with the specified name already exists.
570+
# If the application exists, prompts the user to decide whether to proceed with the
571+
# existing application or exit to create new application with different app_name.
572+
#
573+
# Globals:
574+
# $app_name - The display name of the Azure AD application to check for.
575+
# $AzRetVal - Return value from the last az_wrapper command.
576+
# $AzOutput - Output from the last az_wrapper command.
577+
# $application_id - Set to the App ID if a matching application is found.
578+
#
579+
# Behavior:
580+
# 1. Queries Azure AD for applications matching the specified display name.
581+
# 2. Counts the number of matching applications returned.
582+
# 3. If multiple applications are found with the same name, exits with an error.
583+
# 4. If exactly one application is found, warns the user and prompts for confirmation
584+
# to proceed with the existing application or exit to rename it.
585+
# 5. If no application is found, returns 1 to indicate the application doesn't exist.
586+
#
587+
# Returns:
588+
# 0 if an application with the specified name exists and user chooses to proceed.
589+
# 1 if no application with the specified name exists.
590+
# Exits with error if multiple applications are found or if Azure CLI operations fail.
591+
# ------------------------------------------------------------------------------------
592+
check_if_app_exists() {
593+
lightblue "\nValidating that application with the name: '$app_name', doesn't exist"
594+
az_wrapper ad app list --filter "displayName eq '$app_name'" --query "[].appId" -o tsv
595+
application_id="$AzOutput"
481596

482-
# only single (non multi) tenant mode
483-
create_cloudguard_app_registration() {
597+
local app_result_count
598+
app_result_count=$(echo "$application_id" | grep -c '^')
484599

485-
lightblue "\nCreating a new application for '$onboarding_scope' scope, with role:'Reader', and name:'$app_name'"
486-
# Create a new service principal with the Reader role
487-
local sp_info
488-
az_wrapper ad sp create-for-rbac -n "$app_name" --role "Reader" --scopes "$onboarding_scope" 2>/dev/null
489-
sp_info="$AzOutput"
600+
if [ "$app_result_count" -gt 1 ]; then
601+
exit_with_error "More than one application found with the name $app_name - Please rename the 'app_name' parameter to a different name."
602+
fi
490603

491-
if [ $AzRetVal -ne 0 ]; then
492-
exit_with_error "Failed to create app registration with role Reader. Error from Azure: \n$AzOutput"
604+
if [ -n "$application_id" ]; then
605+
yellow "\nApplication with the name: '$app_name', already exist. We will proceed with the existing application."
606+
check_if_user_would_like_to_proceed "Please rename the 'app_name' parameter to a different name."
607+
return 0
493608
fi
494609

495-
lightblue "\nApplication info: $sp_info"
496-
application_id=$(echo "$sp_info" | jq -r ".appId")
497-
client_secret=$(echo "$sp_info" | jq -r ".password")
610+
return 1
611+
}
498612

613+
# create_cloudguard_app_registration
614+
# --------------------------------------
615+
# Creates or reuses an Azure AD application registration for CloudGuard in single tenant mode.
616+
# This function handles both scenarios: creating a new application or using an existing one.
617+
#
618+
# Globals:
619+
# $app_name - The display name of the Azure AD application to create or reuse.
620+
# $onboarding_scope - The Azure scope (subscription/management group) for the application.
621+
# $application_id - Set to the App ID of the created or existing application.
622+
# $client_secret - Set to the client secret (password) of the application.
623+
# $sp_id - Set to the service principal ID associated with the application.
624+
# $AzRetVal - Return value from the last az_wrapper command.
625+
# $AzOutput - Output from the last az_wrapper command.
626+
# $CLIENT_SECRET_PRINTED_ON_FIRST_RUN - Constant indicating secret was shown previously.
627+
# $NECESSARY_PERMISSIONS_STR - String describing necessary permissions for error messages.
628+
#
629+
# Behavior:
630+
# 1. Checks if an application with the specified name already exists using check_if_app_exists.
631+
# 2. If application exists:
632+
# - Logs a message about proceeding with existing application.
633+
# - Sets client_secret to a placeholder indicating it was shown on first run.
634+
# 3. If application doesn't exist:
635+
# - Creates a new Azure AD application with service principal using az ad sp create-for-rbac.
636+
# - Assigns the "Reader" role to the application for the specified scope.
637+
# - Extracts the application ID and client secret from the creation response.
638+
# 4. Retrieves and stores the service principal ID for the application.
639+
#
640+
# Returns:
641+
# 0 on success.
642+
# Exits with error if application creation fails or service principal ID cannot be retrieved.
643+
#
644+
# Side Effects:
645+
# - Creates a new Azure AD application and service principal if one doesn't exist.
646+
# - Assigns "Reader" role to the application for the specified scope.
647+
# - Sets global variables: $application_id, $client_secret, $sp_id.
648+
# ------------------------------------------------------------------------------------
649+
create_cloudguard_app_registration() {
650+
if check_if_app_exists; then
651+
lightblue "\nApplication with the name: '$app_name', appId: '$application_id' already exists. Proceeding with existing application."
652+
client_secret="$CLIENT_SECRET_PRINTED_ON_FIRST_RUN"
653+
else
654+
lightblue "\nCreating a new application for '$onboarding_scope' scope, with role:'Reader', and name:'$app_name'"
655+
# Create a new service principal with the Reader role
656+
local sp_info
657+
az_wrapper ad sp create-for-rbac -n "$app_name" --role "Reader" --scopes "$onboarding_scope" 2>/dev/null
658+
sp_info="$AzOutput"
659+
660+
if [ $AzRetVal -ne 0 ]; then
661+
exit_with_error "Failed to create app registration with role Reader. Error from Azure: \n$AzOutput"
662+
fi
663+
664+
lightblue "\nApplication info: $sp_info"
665+
application_id=$(echo "$sp_info" | jq -r ".appId")
666+
client_secret=$(echo "$sp_info" | jq -r ".password")
667+
fi
499668

500669
az_wrapper ad sp show --id "$application_id" --query id --output tsv
501670

@@ -543,7 +712,42 @@ service_principal_doesnt_exists() {
543712
}
544713

545714

546-
# Function to create role assignments for CloudGuard app
715+
# create_role_assignments_for_cloudguard_app
716+
# ---------------------------------------------
717+
# Creates role assignments for a CloudGuard application based on the onboarding mode.
718+
# This function assigns the minimum required "Reader" role and additional roles if
719+
# "manage" mode is specified.
720+
#
721+
# Arguments:
722+
# $1 - The application (service principal) ID to assign roles to.
723+
#
724+
# Globals:
725+
# $sp_id - Service principal ID associated with the application (set if not already available).
726+
# $onboarding_scope - The Azure scope (subscription/management group) for role assignments.
727+
# $onboarding_mode - The onboarding mode ("read-only" or "manage").
728+
# $ROLES - Array of roles required for "manage" mode ("Contributor", "User Access Administrator").
729+
# $AzRetVal - Return value from the last az_wrapper command.
730+
# $AzOutput - Output from the last az_wrapper command.
731+
# $NECESSARY_PERMISSIONS_STR - String describing necessary permissions for error messages.
732+
#
733+
# Behavior:
734+
# 1. If $sp_id is not set, retrieves the service principal ID for the given application.
735+
# 2. Always assigns the "Reader" role to the application for the specified scope.
736+
# 3. If onboarding_mode is "manage":
737+
# - Iterates through the ROLES array.
738+
# - Assigns each role ("Contributor", "User Access Administrator") to the application.
739+
# 4. Uses app_add_role_assignment_if_needed to check for existing assignments before creating new ones.
740+
#
741+
# Returns:
742+
# 0 on success.
743+
# Exits with error if service principal ID cannot be retrieved or role assignment fails.
744+
#
745+
# Side Effects:
746+
# - Sets the global variable $sp_id if it was not previously set.
747+
# - Creates role assignments in Azure for the specified application and scope.
748+
# - May create multiple role assignments depending on the onboarding mode.
749+
# ------------------------------------------------------------------------------------
750+
547751
create_role_assignments_for_cloudguard_app() {
548752
local app_id=$1
549753
if [ -z "$sp_id" ]; then
@@ -565,8 +769,22 @@ create_role_assignments_for_cloudguard_app() {
565769
fi
566770
}
567771

772+
# app_add_role_assignment_if_needed
773+
# ------------------------------------------------------------------------------------
774+
# Adds a role assignment to an Azure application if it does not already exist.
775+
#
776+
# Arguments:
777+
# $1 - The application (service principal) ID to assign the role to.
778+
# $2 - The scope at which the role assignment should be applied (e.g., subscription, resource group).
779+
# $3 - The name of the Azure role to assign.
780+
#
781+
# Behavior:
782+
# - Checks if the specified role assignment already exists for the given application and scope.
783+
# - If the assignment exists, logs a message indicating so.
784+
# - If the assignment does not exist, attempts to create it.
785+
# - Exits with an error message if listing or creating the role assignment fails.
786+
# ------------------------------------------------------------------------------------
568787

569-
# Function to add role assignment if needed
570788
app_add_role_assignment_if_needed() {
571789
local app_id=$1
572790
local scope=$2

0 commit comments

Comments
 (0)