-
Notifications
You must be signed in to change notification settings - Fork 327
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #78 from Azure/web_jose
Web UI prototype
- Loading branch information
Showing
17 changed files
with
1,441 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
# Prototype for web-based checklist | ||
|
||
This is a Minimum Viable Product (MVP) for an architecture for web-based checklist reviews. It consists of the following elements: | ||
|
||
- A MySQL database | ||
- An Azure Container Instance that will launch 3 containers: | ||
- filldb (init container): creates the required database and tables in the MySQL server, and fills in the data imported from the latest checklist | ||
- fillgraphdb (init container): executes any Azure Resource Graph queries stored in the checklist, and stores the results in the MySQL database | ||
- flask (main container): a flask-based web frontend that allows inspecting the MysQL checklist table, as well as updating the status and comments of each individual checklist item | ||
|
||
The `fillgraphdb` container needs to authenticate to Azure to send the Azure Resource Graph queries. There are two options: | ||
|
||
- Working today: With Service Principal credentials | ||
- Roadmap: With a [User-Managed Identity](https://docs.microsoft.com/azure/active-directory/managed-identities-azure-resources/overview#how-can-i-use-managed-identities-for-azure-resources) with read access to the subscription(s). The `identityId` parameter of the ARM template needs to be provided. Initial tests have shown that the User-Managed Identity is not available in the init containers. | ||
|
||
The [Azure CLI deployment script for Service Principals](./arm/deploy_sp.azcli) shows how to create the Service Principal, assign the reader role for the whole subscription, and launch the ARM template to create the MySQL server and the Azure Container Instance (it doesn't store the Service Principal secret in an Azure Key Vault, that would be highly advisable). If you already have the Service Principal, you can deploy the ARM template graphically as well using the button below: | ||
|
||
[![Deploy to Azure](https://aka.ms/deploytoazurebutton)](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2FAzure%2Freview-checklists%2Fweb_jose%2Fweb%2Farm%2Ftemplate.json) | ||
|
||
The web interface will be available in the public IP address of the ACI container group, on TCP port 5000. | ||
|
||
## Further improvements | ||
|
||
Since this is only a prototype, there are some aspects not being addressed for the sake of simplicity: | ||
|
||
- Figure out why the user-managed identities seem not be reachable from the init containers | ||
- No HTTPS (it could be easily achieved with an nginx sidecar in the ACI container group) | ||
- No authentication (an authentication proxy such as Ambassador could be leveraged for this) | ||
- The network firewall of the MySQL server is fully open (it could be closed down to the ACI egress IP address) | ||
- The UI of the flask container is rather rudimentary, but it shows the basic principles and does live updates to the MySQL database without having to press any "Submit" button | ||
- SSL Enforcement is disabled in the MySQL Server due to `flask-mysql` not using encryption | ||
- Decouple the containers, so that they can be launched independently: | ||
- It should be possible to launch the `fillgraphdb` container at any time, to refresh the Graph results | ||
- It should be possible to restart the `flask` container (web) without having t | ||
|
||
Contributions highly appreciated! |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
############################################## | ||
# 1. Retrieve Manged Identity, or create one # | ||
############################################## | ||
|
||
id_name=checklistid | ||
id_location=westeurope | ||
id_rg=checklistid | ||
id_id=$(az identity show -n $id_name -g $id_rg --query id -o tsv) | ||
if [[ -z "$id_id" ]]; then | ||
echo "Creating user identity in resource group ${id_rg}..." | ||
az group create -n $id_rg -l $id_location -o none | ||
az identity create -n $id_name -g $id_rg -o none | ||
id_id=$(az identity show -n $id_name -g $id_rg --query id -o tsv) | ||
id_client_id=$(az identity show -n $id_name -g $id_rg --query clientId -o tsv) | ||
subscription_id=$(az account show --query id -o tsv) | ||
scope="/subscriptions/${subscription_id}" | ||
az role assignment create --scope $scope --assignee $id_client_id --role 'Reader' -o none | ||
echo "User-managed identity has been created: $id_id" | ||
else | ||
echo "User-managed identity has been located: $id_id" | ||
fi | ||
|
||
#################################################### | ||
# 2. Create RG and deploy checklist infrastructure # | ||
#################################################### | ||
|
||
# Variables | ||
suffix=$RANDOM | ||
rg="checklist${suffix}" | ||
location=westeurope | ||
aci_name="checklistaci${suffix}" | ||
mysql_server_name="mysql${suffix}" | ||
mysql_server_user=$(whoami) | ||
mysql_server_password=$(echo $(tr -dc a-zA-Z0-9 </dev/urandom 2>/dev/null| head -c 12)) | ||
|
||
# Create RG | ||
echo "Creating resource group ${rg} in ${location}..." | ||
az group create -n $rg -l $location -o none | ||
|
||
# Deploy ARM template | ||
echo "Deploying template to resource group ${rg}..." | ||
az deployment group create -n checklist$RANDOM -g $rg --template-file ./template.json --parameters \ | ||
"checklistTech=aks" \ | ||
"identityId=${id_id}" \ | ||
"aciName=${aci_name}" \ | ||
"serverName=${mysql_server_name}" \ | ||
"administratorLogin=${mysql_server_user}" \ | ||
"administratorLoginPassword=${mysql_server_password}" | ||
|
||
# Optional: re-run fillgraphdb container to refresh the Graph query results | ||
graph_aci_name="graph${suffix}" | ||
graph_image="erjosito/checklist-fillgraphdb:1.0" | ||
mysql_server_fqdn=$(az mysql server show -n $mysql_server_name -g $rg --query 'fullyQualifiedDomainName' -o tsv) | ||
echo "Creating ACI ${graph_aci_name}..." | ||
az container create -n $graph_aci_name -g $rg --assign-identity "$id_id" --image "$graph_image" --ip-address public --restart-policy OnFailure -o none -e \ | ||
"MYSQL_PASSWORD=${mysql_server_password}" \ | ||
"MYSQL_USER=${mysql_server_user}" \ | ||
"MYSQL_FQDN=${mysql_server_fqdn}" \ | ||
"WAIT_INTERVALS=6" | ||
|
||
# Cleanup | ||
# az group delete $rg -y --no-wait |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
################################################################## | ||
# 1. Retrieve SP credentials from Azure Key Vault, or create one # | ||
################################################################## | ||
|
||
# Variables | ||
keyvault_name=erjositoKeyvault | ||
keyvault_rg=keyvaults # Only required if new AKV is to be created | ||
keyvault_loc=westeurope # Only required if new AKV is to be created | ||
purpose=checklists | ||
|
||
# Day zero: create Azure Key Vault if required | ||
echo "Verifying if AKV ${keyvault_name} exists..." | ||
keyvault_rg_found=$(az keyvault list -o tsv --query "[?name=='$keyvault_name'].resourceGroup") | ||
if [[ -n ${keyvault_rg_found} ]] | ||
then | ||
echo "AKV ${keyvault_name} found in resource group $keyvault_rg_found" | ||
keyvault_rg="$keyvault_rg_found" | ||
else | ||
echo "Creating AKV ${keyvault_name} in RG ${keyvault_rg}..." | ||
az group create -n $keyvault_rg -l $keyvault_loc -o none | ||
az keyvault create -n $keyvault_name -g $keyvault_rg -l $keyvault_loc -o none | ||
user_name=$(az account show --query 'user.name' -o tsv) | ||
echo "Setting policies for user ${user_name}..." | ||
az keyvault set-policy -n $keyvault_name -g $keyvault_rg --upn $user_name -o none \ | ||
--certificate-permissions backup create delete deleteissuers get getissuers import list listissuers managecontacts manageissuers purge recover restore setissuers update \ | ||
--key-permissions backup create decrypt delete encrypt get import list purge recover restore sign unwrapKey update verify wrapKey \ | ||
--secret-permissions backup delete get list purge recover restore set \ | ||
--storage-permissions backup delete deletesas get getsas list listsas purge recover regeneratekey restore set setsas update | ||
fi | ||
|
||
# Try to get SP details from AKV | ||
echo "Trying to get secrets from AKV ${keyvault_name}..." | ||
keyvault_appid_secret_name=$purpose-sp-appid | ||
keyvault_password_secret_name=$purpose-sp-secret | ||
sp_app_id=$(az keyvault secret show --vault-name $keyvault_name -n $keyvault_appid_secret_name --query 'value' -o tsv) | ||
sp_app_secret=$(az keyvault secret show --vault-name $keyvault_name -n $keyvault_password_secret_name --query 'value' -o tsv) | ||
|
||
# If either variable is blank, create new SP with the required name | ||
if [[ -z "$sp_app_id" ]] || [[ -z "$sp_app_secret" ]] | ||
then | ||
# Create new SP | ||
echo "Service Principal secrets for ${purpose} not found, creating new Service Principal..." | ||
sp_name=$purpose | ||
sp_output=$(az ad sp create-for-rbac --name $sp_name --skip-assignment 2>/dev/null) | ||
sp_app_id=$(echo $sp_output | jq -r '.appId') | ||
sp_app_secret=$(echo $sp_output | jq -r '.password') | ||
az keyvault secret set --vault-name $keyvault_name --name $keyvault_appid_secret_name --value $sp_app_id -o none | ||
az keyvault secret set --vault-name $keyvault_name --name $keyvault_password_secret_name --value $sp_app_secret -o none | ||
# Optionally, assign Azure RBAC roles (example a RG) or AKV policy (example certificate/secret get if the SP should be able to retrieve certs) | ||
echo "Adding Reader role to new Service Principal..." | ||
subscription_id=$(az account show --query id -o tsv) | ||
scope="/subscriptions/${subscription_id}" | ||
az role assignment create --scope $scope --assignee $sp_app_id --role 'Reader' -o none | ||
fi | ||
|
||
# Verify whether SP has expired, and if so, renew it | ||
sp_end_date=$(az ad app show --id $sp_app_id --query 'passwordCredentials[0].endDate' -o tsv) | ||
sp_end_date=$(date --date="$sp_end_date" +%s) | ||
now=$(date +%s) | ||
if [[ $sp_end_date < $now ]] | ||
then | ||
echo "SP expired, extending one year" | ||
new_password=$(az ad app credential reset --id $sp_app_id --years 1 --query password -o tsv) | ||
az keyvault secret set --vault-name $keyvault_name --name $keyvault_password_secret_name --value $new_password -o none | ||
sp_app_secret=$new_password | ||
else | ||
echo "SP not expired, not required to renew" | ||
fi | ||
|
||
# Optional: test the SP credentials | ||
# tenant_id=$(az account show --query tenantId -o tsv) | ||
# az login --service-principal -u $sp_app_id -p $sp_app_secret --tenant $tenant_id | ||
|
||
#################################################### | ||
# 2. Create RG and deploy checklist infrastructure # | ||
#################################################### | ||
|
||
suffix=$RANDOM | ||
rg="checklist${suffix}" | ||
location=westeurope | ||
aci_name="checklistaci${suffix}" | ||
mysql_server_name="mysql${suffix}" | ||
mysql_server_user=$(whoami) | ||
mysql_server_password=$(echo $(tr -dc a-zA-Z0-9 </dev/urandom 2>/dev/null| head -c 12)) | ||
echo "Creating resource group ${rg} in ${location}..." | ||
az group create -n $rg -l $location -o none | ||
echo "Deploying template to resource group ${rg}..." | ||
tenant_id=$(az account show --query tenantId -o tsv) | ||
az deployment group create -n checklist$RANDOM -g $rg --template-file ./template.json --parameters \ | ||
"checklistTech=aks" \ | ||
"tenantId=${tenant_id}" \ | ||
"clientId=${sp_app_id}" \ | ||
"clientSecret=${sp_app_secret}" \ | ||
"aciName=${aci_name}" \ | ||
"serverName=${mysql_server_name}" \ | ||
"administratorLogin=${mysql_server_user}" \ | ||
"administratorLoginPassword=${mysql_server_password}" | ||
|
||
# Cleanup | ||
# az group delete $rg -y --no-wait |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
{ | ||
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", | ||
"contentVersion": "1.0.0.0", | ||
"parameters": { | ||
"identityName": { | ||
"value": "checklistid" | ||
}, | ||
"serverName": { | ||
"value": "checklistsql1138" | ||
}, | ||
"administratorLogin": { | ||
"value": "jose" | ||
}, | ||
"administratorLoginPassword": { | ||
"reference": { | ||
"keyVault": { | ||
"id": "/subscriptions/e7da9914-9b05-4891-893c-546cb7b0422e/resourceGroups/myKeyvault/providers/Microsoft.KeyVault/vaults/erjositoKeyvault" | ||
}, | ||
"secretName": "defaultPassword" | ||
} | ||
} | ||
} | ||
} |
Oops, something went wrong.