Compare commits
3 Commits
86281742da
...
c6349e2577
| Author | SHA1 | Date | |
|---|---|---|---|
| c6349e2577 | |||
| 4b86f2bd57 | |||
| 2f465a8217 |
5
.gitignore
vendored
5
.gitignore
vendored
@@ -8,7 +8,12 @@
|
||||
|
||||
# Python
|
||||
**/.venv
|
||||
**/.env
|
||||
**/playground.py
|
||||
|
||||
# Azure Secrets and Configuration.
|
||||
/.acr-pat
|
||||
/azure.env
|
||||
|
||||
# MacOS Finder files
|
||||
**/.DS_Store
|
||||
|
||||
@@ -7,6 +7,8 @@ COPY requirements.txt .
|
||||
RUN pip install --root-user-action=ignore --no-cache-dir -r requirements.txt
|
||||
|
||||
COPY image-chooser.py .
|
||||
COPY templates .
|
||||
COPY templates.json .
|
||||
COPY ./entrypoint.sh /
|
||||
|
||||
ENTRYPOINT [ "/entrypoint.sh" ]
|
||||
|
||||
@@ -2,19 +2,53 @@ import streamlit as st
|
||||
import pandas as pd
|
||||
import re
|
||||
from azure.identity import DefaultAzureCredential
|
||||
from azure.mgmt.resource import SubscriptionClient
|
||||
from azure.mgmt.compute import ComputeManagementClient
|
||||
from jinja2 import Template, Environment, FileSystemLoader
|
||||
import json
|
||||
from os import getenv
|
||||
from dotenv import load_dotenv
|
||||
|
||||
load_dotenv()
|
||||
|
||||
def is_valid(key_name: str):
|
||||
return key_name in st.session_state and st.session_state[key_name] is not None and len(st.session_state[key_name]) > 0
|
||||
|
||||
def is_valid_and_not_equal(key_name: str, value: str):
|
||||
return key_name in st.session_state and st.session_state[key_name] != value
|
||||
|
||||
def clear_offers():
|
||||
if 'offers' in st.session_state: del st.session_state.offers
|
||||
if 'selected_offer' in st.session_state: del st.session_state.selected_offer
|
||||
clear_skus()
|
||||
|
||||
def clear_selected_sku():
|
||||
if 'selected_sku' in st.session_state: del st.session_state.selected_sku
|
||||
|
||||
def clear_skus():
|
||||
if 'skus' in st.session_state: del st.session_state.skus
|
||||
clear_selected_sku()
|
||||
if 'selected_sku' in st.session_state: del st.session_state.selected_sku
|
||||
clear_image_versions()
|
||||
|
||||
def clear_image_versions():
|
||||
if 'image_versions' in st.session_state: del st.session_state.image_versions
|
||||
if 'selected_image_version' in st.session_state: del st.session_state.selected_image_version
|
||||
|
||||
def on_publisher_changed():
|
||||
clear_offers()
|
||||
# st.rerun()
|
||||
|
||||
def on_offer_changed():
|
||||
clear_skus()
|
||||
# st.rerun()
|
||||
|
||||
def on_sku_changed():
|
||||
clear_skus()
|
||||
# st.rerun()
|
||||
|
||||
def on_image_version_changed():
|
||||
clear_image_versions()
|
||||
# st.rerun()
|
||||
|
||||
def version_key(v):
|
||||
return [int(x) for x in v.split('.')]
|
||||
|
||||
@st.cache_data
|
||||
def get_publishers(location: str):
|
||||
@@ -32,57 +66,112 @@ def get_skus(location: str, publisher: str, offer: str):
|
||||
def get_image_versions(location: str, publisher: str, offer: str, sku: str):
|
||||
return [version.name for version in compute_client.virtual_machine_images.list(location, publisher, offer, sku)]
|
||||
|
||||
subscription_id = "c885a276-c882-483f-b216-42f73715161d"
|
||||
location = "westeurope"
|
||||
@st.cache_data
|
||||
def get_locations():
|
||||
subscription_client = SubscriptionClient(credential)
|
||||
locations = sorted(subscription_client.subscriptions.list_locations(getenv('AZURE_SUBSCRIPTION_ID')), key=lambda l: l.name)
|
||||
return [
|
||||
loc for loc in locations
|
||||
if loc.metadata.region_type == 'Physical'
|
||||
]
|
||||
|
||||
subscription_id = getenv("AZURE_SUBSCRIPTION_ID")
|
||||
default_location = getenv("AZURE_LOCATION")
|
||||
|
||||
credential = DefaultAzureCredential()
|
||||
|
||||
locations = get_locations()
|
||||
|
||||
compute_client = ComputeManagementClient(credential, subscription_id)
|
||||
|
||||
st.set_page_config(page_title='Azure Image Chooser', layout='wide')
|
||||
|
||||
st.title('Azure Image Chooser')
|
||||
|
||||
left_col, middle_col, right_col = st.columns(3)
|
||||
location_cols = st.columns(4)
|
||||
|
||||
location = location_cols[0].selectbox('Select Azure Location',
|
||||
options=[{"name": loc.name, "display_name": loc.display_name} for loc in locations],
|
||||
index=locations.index(next((l for l in locations if l.name == default_location), default_location)),
|
||||
format_func=lambda loc: loc['display_name']
|
||||
)['name']
|
||||
|
||||
publisher_col, offer_col, sku_col, version_col = st.columns(4)
|
||||
|
||||
# Publishers
|
||||
if 'publishers' not in st.session_state:
|
||||
st.session_state.publishers = get_publishers(location)
|
||||
clear_offers()
|
||||
|
||||
selected_publisher = left_col.selectbox('Select Publisher', options=st.session_state.publishers)
|
||||
if is_valid('publishers'):
|
||||
st.session_state.selected_publisher = publisher_col.selectbox('Select Publisher', options=st.session_state.publishers, on_change=on_publisher_changed, index=None)
|
||||
else:
|
||||
st.error("No publishers found. Please check your Azure subscription and location.")
|
||||
st.stop()
|
||||
|
||||
if 'selected_publisher' not in st.session_state or selected_publisher != st.session_state.selected_publisher:
|
||||
st.session_state.selected_publisher = selected_publisher
|
||||
clear_offers()
|
||||
|
||||
if 'offers' not in st.session_state:
|
||||
# Offers
|
||||
if 'offers' not in st.session_state and is_valid('selected_publisher'):
|
||||
st.session_state.offers = get_offers(location, st.session_state.selected_publisher)
|
||||
clear_skus()
|
||||
|
||||
selected_offer = middle_col.selectbox('Select Offer', options=st.session_state.offers)
|
||||
if is_valid('offers'):
|
||||
st.session_state.selected_offer = offer_col.selectbox('Select Offer', options=st.session_state.offers, on_change=on_offer_changed, index=None)
|
||||
elif is_valid('selected_publisher'):
|
||||
st.info("No offers found for the selected publisher. Please select a different publisher.")
|
||||
st.stop()
|
||||
else:
|
||||
st.stop()
|
||||
|
||||
if 'selected_offer' not in st.session_state or selected_offer != st.session_state.selected_offer:
|
||||
st.session_state.selected_offer = selected_offer
|
||||
clear_skus()
|
||||
|
||||
if 'skus' not in st.session_state:
|
||||
# SKUs
|
||||
if 'skus' not in st.session_state and is_valid('selected_publisher') and is_valid('selected_offer'):
|
||||
st.session_state.skus = get_skus(location, st.session_state.selected_publisher, st.session_state.selected_offer)
|
||||
clear_selected_sku()
|
||||
|
||||
selected_sku = right_col.selectbox('Select SKU', options=st.session_state.skus)
|
||||
|
||||
if 'selected_sku' not in st.session_state or selected_sku != st.session_state.selected_sku:
|
||||
st.session_state.selected_sku = selected_sku
|
||||
|
||||
def version_key(v):
|
||||
return [int(x) for x in v.split('.')]
|
||||
if is_valid('skus'):
|
||||
st.session_state.selected_sku = sku_col.selectbox('Select SKU', options=st.session_state.skus, on_change=on_sku_changed, index=None)
|
||||
elif is_valid('selected_offer'):
|
||||
st.info("No SKUs found for the selected offer. Please select a different offer.")
|
||||
st.stop()
|
||||
else:
|
||||
st.stop()
|
||||
|
||||
# Image versions
|
||||
if 'image_versions' not in st.session_state and is_valid('selected_publisher') and is_valid('selected_offer') and is_valid('selected_sku'):
|
||||
image_versions = get_image_versions(location, st.session_state.selected_publisher, st.session_state.selected_offer, st.session_state.selected_sku)
|
||||
# Check if all image version string match the re.
|
||||
regex = re.compile(r'^[0-9]+\.[0-9]+\.[0-9]+$')
|
||||
|
||||
# Display available image versions
|
||||
images_versions = get_image_versions(location, st.session_state.selected_publisher, st.session_state.selected_offer, st.session_state.selected_sku)
|
||||
if all(regex.match(version) for version in image_versions):
|
||||
image_versions = sorted(image_versions, key=version_key)
|
||||
|
||||
# Check if all image version string match the re.
|
||||
if all(regex.match(version) for version in images_versions):
|
||||
images_versions = sorted(images_versions, key=version_key)
|
||||
st.session_state.image_versions = image_versions
|
||||
|
||||
st.dataframe(images_versions, hide_index=True, column_config={"value": "Image Version"})
|
||||
if is_valid('image_versions'):
|
||||
st.session_state.selected_image_version = version_col.selectbox('Select Image Version', options=st.session_state.image_versions, index=None)
|
||||
elif is_valid('selected_sku'):
|
||||
st.info("No image versions found for the selected SKU. Please select a different SKU.")
|
||||
st.stop()
|
||||
else:
|
||||
st.stop()
|
||||
|
||||
if is_valid('selected_image_version'):
|
||||
st.subheader("Usage example")
|
||||
|
||||
with open("templates.json") as f:
|
||||
templates = json.load(f)
|
||||
|
||||
def usage_scenario_label(item):
|
||||
return item['label']
|
||||
|
||||
layout = st.columns(4)
|
||||
|
||||
selected_file = layout[0].selectbox('Select usage scenario:', options=templates, format_func=usage_scenario_label)
|
||||
|
||||
env = Environment(loader=FileSystemLoader("templates"))
|
||||
tpl = env.get_template(selected_file['file'])
|
||||
rendered = tpl.render(
|
||||
publisher=st.session_state.selected_publisher,
|
||||
offer=st.session_state.selected_offer,
|
||||
sku=st.session_state.selected_sku,
|
||||
version=st.session_state.selected_image_version
|
||||
)
|
||||
|
||||
st.code(rendered, language=selected_file['language'])
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
streamlit
|
||||
azure-identity
|
||||
azure.mgmt.resource
|
||||
azure-mgmt-compute
|
||||
python-dotenv
|
||||
|
||||
17
app/templates.json
Normal file
17
app/templates.json
Normal file
@@ -0,0 +1,17 @@
|
||||
[
|
||||
{
|
||||
"label": "Terraform VM image reference",
|
||||
"language": "hcl",
|
||||
"file": "azurerm_hcl.tpl"
|
||||
},
|
||||
{
|
||||
"label": "Azure CLI",
|
||||
"language": "shell",
|
||||
"file": "shell.tpl"
|
||||
},
|
||||
{
|
||||
"label": "Azure Resource Manager Template",
|
||||
"language": "json",
|
||||
"file": "arm_vm.jsonc"
|
||||
}
|
||||
]
|
||||
17
app/templates/arm_vm.jsonc
Normal file
17
app/templates/arm_vm.jsonc
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
// This is a partial Azure virtual machine resource template.
|
||||
"type": "Microsoft.Compute/virtualMachines",
|
||||
"apiVersion": "2022-03-01",
|
||||
"name": "example-vm",
|
||||
"location": "westeurope",
|
||||
"properties": {
|
||||
"storageProfile": {
|
||||
"imageReference": {
|
||||
"publisher": "{{ publisher }}",
|
||||
"offer": "{{ offer }}",
|
||||
"sku": "{{ sku }}",
|
||||
"version": "{{ version }}"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
6
app/templates/azurerm_hcl.tpl
Normal file
6
app/templates/azurerm_hcl.tpl
Normal file
@@ -0,0 +1,6 @@
|
||||
source_image_reference = {
|
||||
publisher = "{{ publisher }}"
|
||||
offer = "{{ offer }}"
|
||||
sku = "{{ sku }}"
|
||||
version = "{{ version }}"
|
||||
}
|
||||
1
app/templates/shell.tpl
Normal file
1
app/templates/shell.tpl
Normal file
@@ -0,0 +1 @@
|
||||
az create -n MyVM -g MyResourceGroup --image {{ publisher }}:{{ offer }}:{{ sku }}:{{ version }}
|
||||
Reference in New Issue
Block a user