April 2, 2024 Marco

Unlocking Azure Policy innovation using Azure OpenAI

As I started my journey into the world of generative AI, I regularly set my own challenges to myself. One such challenge was to bulk-generate Azure Policy definitions based on simple prompts. If you already had some hands-on with custom policies, you might know the struggle when it comes to developing and testing the policies, especially for more advanced policies using the DeployIfNotExist effect type.

Today’s models are immensely powerful when it comes to code generation, does this also apply to generating Azure Policy definitions? Let’s dive right in and take a deeper look!

The challenge

I want to bulk-generate Azure Policy definitions by leveraging the power of Azure OpenAI and simple prompts. As I have requirements for many different policies, my goal is to have some sort of list with all the prompts to go through and automatically save the output to a JSON file for each policy, in order to process it further. Yes, I could simply generate one by one using ChatGPT or the Azure OpenAI Service playground, but where’s the fun in that?

The game plan

Because we want to generate text or code, we’ll use a GPT model. I’m going to use OpenAI’s GPT-4 model (Generative Pre-trained Transformer 4) since it’s even more advanced in code generation than the already very powerful GPT-3.5. With Azure OpenAI, you can also have multiple model deployments and switch back and forth to compare multiple models like GPT-3.5 and GPT-4.

We will first create a CSV file that holds our prompts and additional metadata, like the policy category in my case. We’ll then create a Python script that

  1. reads the CSV file
  2. loops through each prompt and generates the system and user prompt
  3. sends the request to an existing Azure OpenAI Service model deployment
  4. parses and saves the JSON response to a file

Let’s get started!

Prerequisites

For the following section you can start fresh in an empty folder or download the finished code from my GitHub repository:

To follow along, ensure that you have:

  • An Azure subscription
  • An Azure OpenAI Service with a model deployed (any GPT-3.5 or GPT-4 will do)
    At the time of writing, access to the Azure OpenAI Service is still granted only by application. You can apply for access to Azure OpenAI by completing the form at https://aka.ms/oai/access. Also, make sure to check the supported region for the desired model.
  • Python 3.7+ installed on the platform from where you run the script

Prepare Python environment

We first need to prepare our Python environment with a few requirements. Create a folder on your machine to store the project files and open the folder in Visual Studio Code or a terminal. Run the following commands to create all the necessary files and dependencies:

# Create and active a Python virtual environment
python3 -m venv .venv
.venv\scripts\activate

# Install required Python modules
pip install python-dotenv openai

# Create Python environment file
New-Item -ItemType File -Name .env

# Create an empty Python file that holds the script
New-Item -ItemType File -Name create-azure-policy.py

# Create output folder
New-Item -ItemType Directory -Name output

# Create an empty CSV file that holds the prompts
New-Item -ItemType File -Name azure-policy-prompts.csv

The .env file holds the information to connect to our OpenAI resource. Add the following section and fill in the required values. You can find the values in the OpenAI service in the Azure portal under the section Keys and Endpoint as well as the deployment name in the Azure OpenAI Studio.

AZURE_OAI_ENDPOINT=<your-azure-openai-endpoint>
AZURE_OAI_KEY=<your-azure-openai-key>
AZURE_OAI_DEPLOYMENT=<your-azure-openai-deployment>

Prepare prompts

In the last step, we already created an empty CSV file for our prompts. When adding our prompts, we need to have some idea of what information should be static, in my case the policy category, and what information OpenAI can generate for us. In the beginning, I tended to provide a lot of static values like the policy display name or description, but why don’t we let OpenAI create that for us as well?!

Add the following section to your azure-policy-prompts.csv:

category;prompt
Key Vault;Create an azure policy definition that automatically deploys an azure key vault when a resource group is created. The key vault will be created in the same newly created resource group. Append "-kva" to the name of the resource group for the keyvault name.
Storage;Create an azure policy that automatically creates a container inside a storage account for each newly created storage account. create a parameter for the container name.
Storage;Create an azure policy that automatically enables backup for each new file share that gets created. Add all necessary parameters. Add a possibility to exclude a storage account by a custom tag and value.

Generate Azure Policy definitions

Open the empty Python file create-azure-policy.py in your favorite IDE. Let’s first import some dependencies:

import os
import csv, json
from dotenv import load_dotenv
from openai import AzureOpenAI

We can now add your first function which first creates the client object. Imagine the client object as a connection to the OpenAI service. We then read our CSV file and loop through the rows, invoking another function.

def main(): 
        
    try: 
    
        # Get configuration settings 
        load_dotenv()
        azure_oai_endpoint = os.getenv("AZURE_OAI_ENDPOINT")
        azure_oai_key = os.getenv("AZURE_OAI_KEY")
        azure_oai_deployment = os.getenv("AZURE_OAI_DEPLOYMENT")
        
        # Configure the Azure OpenAI client
        client = AzureOpenAI(
                azure_endpoint = azure_oai_endpoint, 
                api_key=azure_oai_key,  
                api_version="2023-05-15"
                )

        # Reading Azure Policy definitions
        rows = []
        with open('azure-policy-prompts.csv') as csv_file:
            csv_reader = csv.reader(csv_file, delimiter=';')
            header = next(csv_reader)
            for row in csv_reader:
                generate_policy(row[0], row[1], model=azure_oai_deployment, client=client)

    except Exception as ex:
        print(ex)

Add the second function which puts together prompt message, calls the OpenAI service, and saves the response in a file in the output/ folder. Note lines 37 and 38 which ground the model with a system prompt as well as further enhance the prompt from the CSV file.

def generate_policy(category, prompt, model, client):

    # Generate the user and system prompt
    system_message = "You are a developer specialized in Microsoft Azure. You are specialized in Azure Policy and help create custom policy defentitions. You only respond in json and do not include any explanations."
    user_message = f"{prompt} Choose a matching displayname and description inside the policy definition. Use '{category}' as the category."

    # Format the request message
    messages = [
        {"role": "system", "content": system_message},
        {"role": "user", "content": user_message},
    ]

    # Call the Azure OpenAI model
    response = client.chat.completions.create(
        model=model,
        messages=messages,
        temperature=0.6,
        max_tokens=1000,
        top_p=0.95,
        frequency_penalty=0,
        presence_penalty=0,
        stop=None
    )

    # Remove code formatting
    json_formatted = response.choices[0].message.content.replace("```json", "").replace("```", "")

    # Parse the JSON data to get the display name
    json_definition = json.loads(response.choices[0].message.content)
    
    # Extract the file name from the policy definition
    output_file_name = f"{json_definition['properties']['displayName'].replace(' ', '_').lower()}.json"

    # Write the response to a file
    output_file = open(file=f"output/{output_file_name}", mode="w", encoding="utf8")
    output_file.write(response.choices[0].message.content)
    print(f"Successfully created output/{output_file_name}")

Finally, add the following lines to actually execute the functions:

if __name__ == '__main__': 
    main()

 

Now the magic moment has come, let’s run the entire script and see what we get back. As always with Generative AI, never take generated responses as correct and always check back and forth.

python .\create-azure-policy.py

After a few seconds, you should see 3 new JSON files in your output/ folder, each holding an Azure policy definition. Notice that we (hopefully) only have the JSON definition in the file, but no descriptions or other chit-chat answers from our model. This is achieved part using prompting and part parsing of the API response.

Now, can we head straight forward and deploy those policy definitions? Well, maybe. Maybe the policies are correct and working and maybe we need to do some tweaking or see this as a foundation to start developing on.

Please keep in mind:

  • The generate_policy function expects a specific prompt structure and may need adjustments for different use cases
  • During the development of the script, response formats and sections changed from time to time, you might need to refactor the script to new response formats
  • The output directory must exist before running the script
  • The system message in the generate_policy function is hardcoded and should be modified according to the specific requirements

 

The code from this post can be found in my GitHub repository.

Conclusion

I hope this post gave you an insight into how to use Azure OpenAI and how to interact with it in an automated way – and how it can even help in cloud automation tasks in the end.

I think it’s important to state again:

  1. Don’t expect to get 100% correct responses, see it as a foundation or starting point
  2. The response depends on your prompting
  3. Always assume the generated answer to be incorrect and contain false information. Always check the answers for accuracy.

Take care and happy generating!

, , ,
Marco Gerber

Marco

Senior Cloud Engineer, keen on Azure and cloud technologies.

Leave a Reply

Your email address will not be published. Required fields are marked *