From 528e33a18af028f1bbc24e98fd770ec635a3d4de Mon Sep 17 00:00:00 2001 From: Gal Szkolnik Date: Sat, 29 Jul 2023 04:23:19 +0000 Subject: [PATCH] Assignment 02 code + documentation --- src/ASSIGNMENT-02/ASSIGNMENT-02.md | 59 +++++++++++++++++++++++ src/ASSIGNMENT-02/GetSecret/__init__.py | 62 ++++++++++++++++++++++--- src/ASSIGNMENT-02/GetSecret/sample.dat | 2 +- src/ASSIGNMENT-02/requirements.txt | 23 +++++++++ 4 files changed, 139 insertions(+), 7 deletions(-) create mode 100644 src/ASSIGNMENT-02/ASSIGNMENT-02.md create mode 100644 src/ASSIGNMENT-02/requirements.txt diff --git a/src/ASSIGNMENT-02/ASSIGNMENT-02.md b/src/ASSIGNMENT-02/ASSIGNMENT-02.md new file mode 100644 index 0000000..ae0b7f3 --- /dev/null +++ b/src/ASSIGNMENT-02/ASSIGNMENT-02.md @@ -0,0 +1,59 @@ +# 2nd Assignment + +## Main Challanges + +Azure is a new envionrment for me, but my experience within GCP has been +helpful in navigating my needs. + +I've never deployed function apps before (not even in GCP), but the +concept was rather clear to me. + +I learned how to creat a System Assigned Managed Role to the azure func. +I also learned how to assign it to the Key Valult's Secret-User roles. +(I did this manually on each vault, as the free account does not allow +creation of custom roles, which is probably what I would use in a +production envrionment) + +I learned how to allow Visual Studio to create the function app and +deploy the code. Something I had to troubleshoot at first, as my first +setup failed to deploy multiple times. +Seems that newbies to this realm, based on my searching for solutions, +face similar issues - but I eventually overcame those hurdles by +correctly deploying a fresh Funciton App. + +## Script logic + +The script itself is rather simple, I based it on the template HTTP +trigger function from the VS Code template. + +I added authentication and the KeyVault logic, and added error handling +and reporting code. + +After a successful local run, I created a requirements.txt file and +deploeyd the function app to Azure. + +### Notes about current implementation + +In a production public (without authentication of any sorts) facing page +I would not leave the error reporting code as it is, and rely more on +logging, but since I'm unfamiliar with the logging constructs preferred +I ommitted this at this time. + +## How to use: + +The App's URL is: + +> + +It takes a single argument `name` which is the Vault's name to pull the +secret from (GSzVaronisAssignmentKv1, GSzVaronisAssignmentKv2 or +GSzVaronisAssignmentKv3) + +For example: + +> + +When name is not supplied, some identifying details are presented for +troubleshooting purposes. + +When an exception occured, the error message will be printed. diff --git a/src/ASSIGNMENT-02/GetSecret/__init__.py b/src/ASSIGNMENT-02/GetSecret/__init__.py index d493dfe..8c8ce6c 100644 --- a/src/ASSIGNMENT-02/GetSecret/__init__.py +++ b/src/ASSIGNMENT-02/GetSecret/__init__.py @@ -1,12 +1,24 @@ import logging +import sys +# Code based on VSCode template for Python Azure Function Apps and +# https://learn.microsoft.com/en-us/azure/key-vault/secrets/quick-create-python?tabs=azure-cli#create-the-sample-code + +import os import azure.functions as func +from azure.keyvault.secrets import SecretClient +from azure.identity import DefaultAzureCredential def main(req: func.HttpRequest) -> func.HttpResponse: logging.info('Python HTTP trigger function processed a request.') name = req.params.get('name') + credential = None + Err = None + msg = "" + status_code = 200 if not name: + status_code = 201 try: req_body = req.get_json() except ValueError: @@ -14,10 +26,48 @@ def main(req: func.HttpRequest) -> func.HttpResponse: else: name = req_body.get('name') - if name: - return func.HttpResponse(f"Hello World, {name}!") + try: + credential = DefaultAzureCredential() + except: + the_type, Err, the_traceback = sys.exc_info() + status_code = 500 + credential = None + pass + + # except BaseException as e: + # return func.HttpResponse( e, status_code=200 ) + + # credentialErr = e + + + if name and credential: + keyVaultName = name + KVUri = f"https://{keyVaultName}.vault.azure.net" + + try: + client = SecretClient(vault_url=KVUri, credential=credential) + + secretName = "VaronisAssignmentSecret" + + print(f"Retrieving your secret from {keyVaultName}.") + + retrieved_secret = client.get_secret(secretName) + + print(f"Your secret is '{retrieved_secret.value}'.") + + msg = f"{retrieved_secret.value}" + except: + status_code = 500 + the_type, Err, the_traceback = sys.exc_info() + pass else: - return func.HttpResponse( - "Please pass a name on the query string or in the request body", - status_code=400 - ) \ No newline at end of file + msg = "This HTTP triggered function executed successfully. Pass a name in the query string or in the request body for a personalized response." + + if status_code != 200: + if credential: + msg += f"\nCredentials { credential }." + + if Err: + msg += f"\nErr: { Err }" + + return func.HttpResponse( msg, status_code=status_code ) diff --git a/src/ASSIGNMENT-02/GetSecret/sample.dat b/src/ASSIGNMENT-02/GetSecret/sample.dat index 2e60943..7afe080 100644 --- a/src/ASSIGNMENT-02/GetSecret/sample.dat +++ b/src/ASSIGNMENT-02/GetSecret/sample.dat @@ -1,3 +1,3 @@ { - "name": "Azure" + "name": "GSzVaronisAssignmentKv1" } \ No newline at end of file diff --git a/src/ASSIGNMENT-02/requirements.txt b/src/ASSIGNMENT-02/requirements.txt new file mode 100644 index 0000000..8949c60 --- /dev/null +++ b/src/ASSIGNMENT-02/requirements.txt @@ -0,0 +1,23 @@ +azure-common==1.1.28 +azure-core==1.28.0 +azure-functions==1.15.0 +azure-identity==1.13.0 +azure-keyvault==4.2.0 +azure-keyvault-certificates==4.7.0 +azure-keyvault-keys==4.8.0 +azure-keyvault-secrets==4.7.0 +certifi==2023.7.22 +cffi==1.15.1 +charset-normalizer==3.2.0 +cryptography==41.0.2 +idna==3.4 +isodate==0.6.1 +msal==1.23.0 +msal-extensions==1.0.0 +portalocker==2.7.0 +pycparser==2.21 +PyJWT==2.8.0 +requests==2.31.0 +six==1.16.0 +typing_extensions==4.7.1 +urllib3==2.0.4