Skip to content

Commit f9dbec3

Browse files
committed
make use of nginx cli extension for config updates
This change enables the use of "az nginx deployment" cli to do configuration updates. This helps limit the permissions needed by the service principal to do config updates. This change also adds support for the protected files feature supported by NGINXaaS. Users can give a new optional input called protected-files that contains a comma separated list of all the files that need to be marked as protected. For more information, visit: https://docs.nginx.com/nginxaas/azure/getting-started/nginx-configuration/nginx-configuration-portal/#add-an-nginx-configuration
1 parent 14cc47c commit f9dbec3

File tree

2 files changed

+214
-60
lines changed

2 files changed

+214
-60
lines changed

action.yml

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,19 @@ inputs:
2424
default: "nginx.conf"
2525
transformed-nginx-config-directory-path:
2626
description: >
27-
'The transformed absolute path of the NGINX configuration directory in NGINXaaS for Azure deployment, example: "/etc/nginx/".
28-
If the "include" directive in the NGINX configuration files uses absolute paths, the path transformation
29-
can be used to overwrite the file paths when the action synchronizes the files to the NGINXaaS for Azure deployment.'
27+
'The absolute directory path in the NGINXaaS for Azure deployment where your configuration files will be placed.
28+
All files found in the nginx-config-directory-path will be copied to this location in the deployment.
29+
For example, use "/etc/nginx/" to match the standard NGINX directory structure on Azure.
30+
If your NGINX configuration files use absolute paths in "include" directives, this setting ensures those paths are correctly mapped in the deployment by prepending the specified directory.'
3031
required: false
3132
default: ""
3233
nginx-certificates:
3334
description: 'An array of JSON objects each with keys nginx_cert_name, keyvault_secret, certificate_virtual_path and key_virtual_path. Example: [{"certificateName": "server1", "keyvaultSecret": "https://...", "certificateVirtualPath": "/etc/ssl/certs/server1.crt", "keyVirtualPath": "/etc/ssl/certs/server1.key" }, {"name": "server2", "keyvaultSecret": "https://...", "certificateVirtualPath": "/etc/ssl/certs/server2.crt", "keyVirtualPath": "/etc/ssl/certs/server2.key" }] '
3435
required: false
36+
protected-files:
37+
description: "Comma-separated list of file paths relative to nginx-config-directory-path that should be marked as protected. Example: 'ssl/private.key,conf.d/secrets.conf'"
38+
required: false
39+
default: ""
3540
debug:
3641
description: "Enable/Disable debug output."
3742
required: false
@@ -40,10 +45,10 @@ runs:
4045
using: "composite"
4146
steps:
4247
- name: "Synchronize NGINX certificate(s) from the Git repository to an NGINXaaS for Azure deployment"
43-
run: ${{github.action_path}}/src/deploy-certificate.sh --subscription_id=${{ inputs.subscription-id }} --resource_group_name=${{ inputs.resource-group-name }} --nginx_deployment_name=${{ inputs.nginx-deployment-name }} --nginx_resource_location=${{ inputs.nginx-deployment-location }} --certificates=${{ toJSON(inputs.nginx-certificates) }} --debug=${{ inputs.debug }}
48+
run: ${{github.action_path}}/src/deploy-certificate.sh --subscription-id=${{ inputs.subscription-id }} --resource-group-name=${{ inputs.resource-group-name }} --nginx-deployment-name=${{ inputs.nginx-deployment-name }} --nginx-resource-location=${{ inputs.nginx-deployment-location }} --certificates=${{ toJSON(inputs.nginx-certificates) }} --debug=${{ inputs.debug }}
4449
if: ${{ inputs.nginx-deployment-location != '' && inputs.nginx-certificates != '' }}
4550
shell: bash
4651
- name: "Synchronize NGINX configuration from the Git repository to an NGINXaaS for Azure deployment"
47-
run: ${{github.action_path}}/src/deploy-config.sh --subscription_id=${{ inputs.subscription-id }} --resource_group_name=${{ inputs.resource-group-name }} --nginx_deployment_name=${{ inputs.nginx-deployment-name }} --config_dir_path=${{ inputs.nginx-config-directory-path }} --root_config_file=${{ inputs.nginx-root-config-file }} --transformed_config_dir_path=${{ inputs.transformed-nginx-config-directory-path }} --debug=${{ inputs.debug }}
52+
run: ${{github.action_path}}/src/deploy-config.sh --subscription-id=${{ inputs.subscription-id }} --resource-group-name=${{ inputs.resource-group-name }} --nginx-deployment-name=${{ inputs.nginx-deployment-name }} --nginx-config-directory-path=${{ inputs.nginx-config-directory-path }} --nginx-root-config-file=${{ inputs.nginx-root-config-file }} --transformed-nginx-config-directory-path=${{ inputs.transformed-nginx-config-directory-path }} --protected-files=${{ inputs.protected-files }} --debug=${{ inputs.debug }}
4853
if: ${{ inputs.nginx-config-directory-path != '' }}
4954
shell: bash

src/deploy-config.sh

Lines changed: 204 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,69 +1,72 @@
11
#!/bin/bash
2-
set -euo pipefail
2+
set -eo pipefail
33
IFS=$'\n\t'
44

55
transformed_config_dir_path=''
66
for i in "$@"
77
do
88
case $i in
9-
--subscription_id=*)
9+
--subscription-id=*)
1010
subscription_id="${i#*=}"
1111
shift
1212
;;
13-
--resource_group_name=*)
13+
--resource-group-name=*)
1414
resource_group_name="${i#*=}"
1515
shift
1616
;;
17-
--nginx_deployment_name=*)
17+
--nginx-deployment-name=*)
1818
nginx_deployment_name="${i#*=}"
1919
shift
2020
;;
21-
--config_dir_path=*)
21+
--nginx-config-directory-path=*)
2222
config_dir_path="${i#*=}"
2323
shift
2424
;;
25-
--root_config_file=*)
25+
--nginx-root-config-file=*)
2626
root_config_file="${i#*=}"
2727
shift
2828
;;
29-
--transformed_config_dir_path=*)
29+
--transformed-nginx-config-directory-path=*)
3030
transformed_config_dir_path="${i#*=}"
3131
shift
3232
;;
3333
--debug=*)
3434
debug="${i#*=}"
3535
shift
3636
;;
37+
--protected-files=*)
38+
protected_files="${i#*=}"
39+
shift
40+
;;
3741
*)
38-
echo "Not matched option '${i#*=}' passed in."
42+
echo "Unknown option '${i}' passed in."
3943
exit 1
4044
;;
4145
esac
4246
done
4347

44-
if [[ ! -v subscription_id ]];
45-
then
46-
echo "Please set 'subscription-id' ..."
47-
exit 1
48+
# Validate Required Parameters
49+
missing_params=()
50+
if [ -z "$subscription_id" ]; then
51+
missing_params+=("subscription-id")
4852
fi
49-
if [[ ! -v resource_group_name ]];
50-
then
51-
echo "Please set 'resource-group-name' ..."
52-
exit 1
53+
if [ -z "$resource_group_name" ]; then
54+
missing_params+=("resource-group-name")
5355
fi
54-
if [[ ! -v nginx_deployment_name ]];
55-
then
56-
echo "Please set 'nginx-deployment-name' ..."
57-
exit 1
56+
if [ -z "$nginx_deployment_name" ]; then
57+
missing_params+=("nginx-deployment-name")
5858
fi
59-
if [[ ! -v config_dir_path ]];
60-
then
61-
echo "Please set 'nginx-config-directory-path' ..."
62-
exit 1
59+
if [ -z "$config_dir_path" ]; then
60+
missing_params+=("nginx-config-directory-path")
6361
fi
64-
if [[ ! -v root_config_file ]];
65-
then
66-
echo "Please set 'nginx-root-config-file' ..."
62+
if [ -z "$root_config_file" ]; then
63+
missing_params+=("nginx-root-config-file")
64+
fi
65+
66+
# Check and print if any required params are missing
67+
if [ ${#missing_params[@]} -gt 0 ]; then
68+
echo "Error: Missing required variables in the workflow:"
69+
echo "${missing_params[*]}"
6770
exit 1
6871
fi
6972

@@ -121,60 +124,206 @@ fi
121124
transformed_root_config_file_path="$transformed_config_dir_path$root_config_file"
122125
echo "The transformed root NGINX configuration file path is '$transformed_root_config_file_path'."
123126

124-
# Create a NGINX configuration tarball.
127+
# Common utility functions
128+
129+
# Function to trim whitespace from a string
130+
trim_whitespace() {
131+
local var="$1"
132+
# Trim leading whitespace from the file path (var)
133+
# ${var%%[![:space:]]*} starts at the file path's end
134+
# and finds the longest match of non-whitespace
135+
# characters leaving only leading whitespaces
136+
# ${var#"..." } removes the leading whitespace found
137+
var="${var#"${var%%[![:space:]]*}"}"
138+
# Remove trailing whitespace
139+
# See explanation above. The process is reversed here.
140+
var="${var%"${var##*[![:space:]]}"}"
141+
# Check if the file exists in the repository
142+
echo "$var"
143+
}
144+
145+
# Function to encode file content to base64
146+
encode_file_base64() {
147+
local file_path="$1"
148+
# Use base64 to encode the file content
149+
# -w 0 option is used to avoid line wrapping in the output
150+
base64 -w 0 "$file_path"
151+
}
125152

126-
config_tarball="nginx-config.tar.gz"
153+
# Function to build virtual path from relative path
154+
build_virtual_path() {
155+
local relative_path="$1"
156+
echo "${transformed_config_dir_path}${relative_path}"
157+
}
127158

128-
echo "Creating a tarball from the NGINX configuration directory."
129-
tar -cvzf "$config_tarball" -C "$config_dir_path" --xform s:'./':"$transformed_config_dir_path": .
130-
echo "Successfully created the tarball from the NGINX configuration directory."
159+
# Function to add a file entry to a JSON array
160+
# The add_file_to_json_array function uses indirect variable references
161+
# and global assignment to update JSON arrays and flags that track
162+
# which files have been processed. The variable names for the JSON array
163+
# and the "first file" flag are passed as arguments, allowing the
164+
# function to generically update different arrays
165+
# (for regular and protected files) without hardcoding their names.
166+
# The syntax ${!var} retrieves the value of the variable whose
167+
# name is stored in 'var', and declare -g ensures the updated
168+
# values are set globally, so changes persist outside the function.
169+
add_file_to_json_array() {
170+
local file_path="$1"
171+
local virtual_path="$2"
172+
local file_type="$3" # "regular" or "protected"
173+
local json_var_name="$4" # Variable name to modify
174+
local first_file_var_name="$5" # Variable name for first_file flag
175+
176+
if [ -f "$file_path" ]; then
177+
echo "Processing $file_type file: $file_path -> $virtual_path"
178+
179+
# Base64 encode the file content
180+
local file_content_b64
181+
file_content_b64=$(encode_file_base64 "$file_path")
182+
183+
# Get current values using indirect variable references
184+
local current_json="${!json_var_name}"
185+
local is_first_file="${!first_file_var_name}"
186+
187+
# Add comma separator if not the first file
188+
if [ "$is_first_file" = false ]; then
189+
current_json+=","
190+
fi
191+
192+
# Add the file entry to JSON array
193+
current_json+="{\"content\":\"$file_content_b64\",\"virtual-path\":\"$virtual_path\"}"
194+
195+
# Update the variables using indirect assignment
196+
declare -g "$json_var_name=$current_json"
197+
declare -g "$first_file_var_name=false"
198+
199+
if [[ "$debug" == true ]]; then
200+
echo "$file_type file virtual path: $virtual_path"
201+
echo "$file_type file content (base64): ${file_content_b64:0:50}..."
202+
fi
203+
else
204+
echo "Warning: $file_type file '$file_path' not found"
205+
fi
206+
}
207+
208+
# Process protected files first to build exclusion list
209+
protected_files_list=()
210+
if [ -n "$protected_files" ]; then
211+
IFS=',' read -ra files <<< "$protected_files"
212+
213+
for file in "${files[@]}"; do
214+
file=$(trim_whitespace "$file")
215+
if [ -n "$file" ]; then
216+
protected_files_list+=("$file")
217+
fi
218+
done
219+
fi
220+
221+
# Function to check if a file is in the protected files list
222+
is_protected_file() {
223+
local relative_path="$1"
224+
for protected_file in "${protected_files_list[@]}"; do
225+
if [ "$relative_path" = "$protected_file" ]; then
226+
return 0 # File is protected
227+
fi
228+
done
229+
return 1 # File is not protected
230+
}
131231

132-
echo "Listing the NGINX configuration file paths in the tarball."
133-
tar -tf "$config_tarball"
232+
# Process all configuration files individually (excluding protected files)
233+
234+
echo "Processing NGINX configuration files individually."
235+
236+
# Build the files JSON array
237+
files_json="["
238+
files_first_file=true
239+
240+
# Find all files in the config directory and process them (excluding protected files)
241+
while IFS= read -r -d '' file; do
242+
# Get relative path from config directory
243+
relative_path="${file#$config_dir_path}"
244+
245+
# Skip if this file is in the protected files list
246+
if is_protected_file "$relative_path"; then
247+
echo "Skipping protected file from regular files: $relative_path"
248+
continue
249+
fi
250+
251+
# Apply transformation to get virtual path
252+
virtual_path=$(build_virtual_path "$relative_path")
253+
254+
add_file_to_json_array "$file" "$virtual_path" "regular" "files_json" "files_first_file"
255+
done < <(find "$config_dir_path" -type f -print0)
134256

135-
encoded_config_tarball=$(base64 "$config_tarball")
257+
files_json+="]"
136258

137259
if [[ "$debug" == true ]]; then
138-
echo "The base64 encoded NGINX configuration tarball"
139-
echo "$encoded_config_tarball"
260+
echo "Regular files JSON: $files_json"
140261
fi
141-
echo ""
142262

143-
# Synchronize the NGINX configuration tarball to the NGINXaaS for Azure deployment.
263+
# Process protected files if specified
264+
protected_files_arg=""
265+
if [ -n "$protected_files" ]; then
266+
echo "Processing protected files: $protected_files"
267+
268+
# Build the protected files JSON array
269+
protected_files_json="["
270+
protected_first_file=true
271+
IFS=',' read -ra files <<< "$protected_files"
272+
273+
for file in "${files[@]}"; do
274+
file=$(trim_whitespace "$file")
275+
if [ -n "$file" ]; then
276+
repo_file_path="${config_dir_path}${file}"
277+
virtual_path=$(build_virtual_path "$file")
278+
279+
add_file_to_json_array "$repo_file_path" "$virtual_path" "protected" "protected_files_json" "protected_first_file"
280+
fi
281+
done
282+
283+
protected_files_json+="]"
284+
285+
if [ "$protected_first_file" = false ]; then
286+
protected_files_arg="--protected-files"
287+
if [[ "$debug" == true ]]; then
288+
echo "Protected files JSON: $protected_files_json"
289+
fi
290+
fi
291+
fi
144292

145-
uuid="$(cat /proc/sys/kernel/random/uuid)"
146-
template_file="template-$uuid.json"
147-
template_deployment_name="${nginx_deployment_name:0:20}-$uuid"
148293

149-
wget -O "$template_file" https://raw.githubusercontent.com/nginxinc/nginx-for-azure-deploy-action/487d1394d6115d4f42ece6200cbd20859595557d/src/nginx-for-azure-configuration-template.json
150-
echo "Downloaded the ARM template for synchronizing NGINX configuration."
151-
cat "$template_file"
152-
echo ""
294+
# Synchronize the NGINX configuration files to the NGINXaaS for Azure deployment.
153295

154296
echo "Synchronizing NGINX configuration"
155297
echo "Subscription ID: $subscription_id"
156298
echo "Resource group name: $resource_group_name"
157299
echo "NGINXaaS for Azure deployment name: $nginx_deployment_name"
158-
echo "ARM template deployment name: $template_deployment_name"
159300
echo ""
160301

161302
az account set -s "$subscription_id" --verbose
162303

304+
echo "Installing the az nginx extension if not already installed."
305+
az extension add --name nginx --allow-preview true
306+
163307
az_cmd=(
164308
"az"
309+
"nginx"
165310
"deployment"
166-
"group"
167-
"create"
168-
"--name" "$template_deployment_name"
311+
"configuration"
312+
"update"
313+
"--name" "default"
314+
"--deployment-name" "$nginx_deployment_name"
169315
"--resource-group" "$resource_group_name"
170-
"--template-file" "$template_file"
171-
"--parameters"
172-
"nginxDeploymentName=$nginx_deployment_name"
173-
"rootFile=$transformed_root_config_file_path"
174-
"tarball=$encoded_config_tarball"
316+
"--root-file" "$transformed_root_config_file_path"
317+
"--files" "$files_json"
175318
"--verbose"
176319
)
177320

321+
# Add protected files argument if present
322+
if [ -n "$protected_files_arg" ]; then
323+
az_cmd+=("$protected_files_arg")
324+
az_cmd+=("$protected_files_json")
325+
fi
326+
178327
if [[ "$debug" == true ]]; then
179328
az_cmd+=("--debug")
180329
echo "${az_cmd[@]}"

0 commit comments

Comments
 (0)