Microsoft, yeni fal MiniMax H3 Max video modelini ve Foundry görsel modellerini (gpt-image, FLUX, MAI Image) iki özel node ve anahtarsız Microsoft Entra ID kimlik doğrulamasıyla ComfyUI’a getirmeyi anlatan bir rehber yayımladı. Sonuç, metinden sinematik bir görselden videoya hattı. GPU ya da API anahtarı gerekmiyor.
ComfyUI, birçok yaratıcı ekibin üretken modelleri zincirlemek için kullandığı node tabanlı bir tuval. Rehberde şu adımlar var: modelleri tek bir Foundry projesinde dağıtmak, ComfyUI’a Entra ID ile anahtarsız erişim vermek, Foundry node’larını eklemek ve bir metin prompt’unu gerçekçi, 5 saniyelik sinematik bir çekime dönüştüren hattı kurmak. Rehber, ComfyUI’ın kurulu olduğunu varsayıyor; Windows, macOS, Linux ya da Azure’daki bir VM üzerinde çalışabilir.

Nasıl çalışıyor?

Node’lar Foundry API’lerini doğrudan çağırıyor. Üretim Foundry’de yapıldığı ve ComfyUI yalnızca çağrıları yönettiği için yalnızca CPU’lu bir makine yeterli. Node’lar, ComfyUI’ın çalıştığı yerdeki kimlikten Entra ID token’ı alıyor: kendi makinenizde az login oturumunuz, Azure’da ise bir managed identity. Paylaşılan iş akışlarında saklanacak, yenilenecek ya da sızacak API anahtarı yok.
Adım 1: Modelleri Foundry projesinde dağıtın
Projenizin arkasındaki Foundry kaynağına en az bir görsel modeli ve fal video modelini dağıtın. Portalda model kataloğunu açıp modeli bulun (Inference tasks filtresinde Text to image seçilebilir), Deploy’u seçip varsayılan ayarları koruyun.

Komut satırını tercih ederseniz her model için şu komutu çalıştırın:
az cognitiveservices account deployment create \
--resource-group <resource-group> \
--name <foundry-resource> \
--deployment-name <deployment-name> \
--model-name <model-name> \
--model-format <model-format> \
--model-version <model-version> \
--sku-name GlobalStandard \
--sku-capacity <capacity>| Model | –model-name | –model-format |
|---|---|---|
| gpt-image | gpt-image-2.5-sunburst | OpenAI |
| FLUX | FLUX.2-pro | Black Forest Labs |
| MAI Image | MAI-Image-2.6 | Microsoft |
| fal MiniMax H3 Max (video) | Fal-MiniMax-H3-Max | fal.ai |
- Kullanılabilir
<model-version>değerini bulmak için:az cognitiveservices account list-models -g <resource-group> -n <foundry-resource> --query "[?name=='<model-name>'].version" -o tsv - Her görsel dağıtımına modelin adını verin (örneğin
FLUX.2-pro); node’lar model ailesini adından tanıyor. - Video dağıtımının adı serbest; rehberde
minimax-h3kullanılıyor.

Son olarak Foundry portalında projenin Overview sayfasından proje uç noktasını kopyalayın. Biçimi: https://<foundry-resource>.services.ai.azure.com/api/projects/<project>
Adım 2: ComfyUI’a anahtarsız erişim verin
Node’lar, ComfyUI’ın çalıştığı makinedeki Entra ID kimliğini kullanan DefaultAzureCredential ile çalışıyor. Bu kimliğin Foundry kaynağında Cognitive Services User rolüne sahip olması gerekiyor; Foundry User rolü de çalışıyor.
ComfyUI kendi makinenizde çalışıyorsa: Azure CLI’ı kurun, ComfyUI’ı başlatan işletim sistemi kullanıcısıyla az login yapın ve rolü atayın:
az login
FOUNDRY_ID=$(az cognitiveservices account show -g <resource-group> -n <foundry-resource> --query id -o tsv)
az role assignment create --assignee-object-id $(az ad signed-in-user show --query id -o tsv) \
--assignee-principal-type User --role "Cognitive Services User" --scope $FOUNDRY_IDRol atama komutları Bash söz dizimi kullanıyor; Windows’ta Git Bash ya da Azure Cloud Shell’de çalıştırılabilir. Yalnızca az login ComfyUI makinesinde çalışmalı.
ComfyUI Azure’da çalışıyorsa: Kişisel oturum yerine managed identity kullanın. Örneğin bir VM’de:
az vm identity assign -g <vm-resource-group> -n <vm-name>
PRINCIPAL_ID=$(az vm show -g <vm-resource-group> -n <vm-name> --query identity.principalId -o tsv)
FOUNDRY_ID=$(az cognitiveservices account show -g <resource-group> -n <foundry-resource> --query id -o tsv)
az role assignment create --assignee-object-id $PRINCIPAL_ID \
--assignee-principal-type ServicePrincipal --role "Cognitive Services User" --scope $FOUNDRY_ID
Rol atamak için kaynakta Owner ya da User Access Administrator rolü gerekiyor; yoksa Azure yöneticinizden isteyin. Atamaların yayılması birkaç dakika sürebilir; ilk çalıştırmada 401 ya da 403 alırsanız bekleyip tekrar deneyin.
Adım 3: Foundry node’larını ComfyUI’a ekleyin
Bir ComfyUI özel node’u, ComfyUI’ın custom_nodes klasöründeki bir Python klasöründen ibaret. İki dosyalı bir klasör oluşturup iki Python paketi kuracak, bir ortam değişkeni ayarlayacak ve ComfyUI’ı yeniden başlatacaksınız.
3.1 ComfyUI klasörünü ve Python’unu bulun:
| Kurulum | custom_nodes nerede? | ComfyUI’ı çalıştıran Python |
|---|---|---|
| ComfyUI Desktop (Windows, macOS) | Instance’ta ⋮, sonra Manage → About; kurulum yolunu açın, custom_nodes içinde | Manage → Terminal, instance klasöründe bir kabuk açar |
| Windows portable | ComfyUI_windows_portable\ComfyUI\custom_nodes | ComfyUI_windows_portable\python_embeded\python.exe |
| Elle kurulum (git clone) | ComfyUI/custom_nodes | ComfyUI’ı başlattığınız sanal ortam, örneğin ComfyUI/.venv |
ComfyUI klasöründe bir terminal açın (elle kurulumda önce sanal ortamı etkinleştirin) ve python‘un sistemin değil ComfyUI’ın Python’u olduğunu kontrol edin. Yazdırılan yol ComfyUI kurulumunun içinde olmalı; değilse aşağıdaki komutlarda tablodaki tam yolu kullanın.
python -c "import sys; print(sys.executable)"3.2 Node klasörünü ve dosyalarını oluşturun. Sonuçta klasör şöyle görünmeli:
ComfyUI/
└── custom_nodes/
└── ComfyUI-Foundry/
├── __init__.py
└── nodes.pycustom_nodes içinde ComfyUI-Foundry adlı bir klasör oluşturun. İçine bu tek satırlık __init__.py dosyasını ekleyin; ComfyUI’a klasörün hangi node’ları sağladığını söylüyor:
from .nodes import NODE_CLASS_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGSAynı klasörde nodes.py oluşturup aşağıdaki kodu yapıştırın. Visual Studio Code gibi bir kod editörü kullanın; Windows Not Defteri’ni kullanırsanız “Farklı kaydet türü”nü Tüm dosyalar yapın, aksi halde dosya nodes.py.txt olarak kaydedilir ve ComfyUI onu yok sayar.
| Node | Ne çağırıyor? |
|---|---|
| Foundry Image Generate | Dağıtıma uygun görsel API’si: gpt-image için /openai/v1/images/generations, FLUX için /providers/blackforestlabs/v1/…, MAI Image için /mai/v1/images/generations |
| fal MiniMax H3 Max Video (Foundry) | /providers/falai/v1/minimax/h3-max/image-to-video; görsel bağlı değilse text-to-video, referans görsel ya da video bağlıysa reference-to-video. fal işleri asenkron olduğu için node video hazır olana kadar sorgulayıp MP4’ü kaydediyor. |
"""ComfyUI nodes for Microsoft Foundry image models and fal video models on Foundry.
Keyless: DefaultAzureCredential uses your az login sign-in, or a managed identity on Azure."""
import base64, io, os, re, time, uuid
import numpy as np
import requests
import torch
from azure.identity import DefaultAzureCredential
from PIL import Image
import folder_paths
try:
from comfy_api.input_impl import VideoFromFile
except ImportError: # older ComfyUI builds without the VIDEO type
VideoFromFile = None
SCOPE = "https://cognitiveservices.azure.com/.default"
_credential = None
def _auth():
global _credential
if _credential is None:
_credential = DefaultAzureCredential(exclude_interactive_browser_credential=True)
return {"Authorization": "Bearer " + _credential.get_token(SCOPE).token}
def _base_url(override=""):
# Accepts a project endpoint (https://<resource>.services.ai.azure.com/api/projects/<project>)
# or the resource endpoint; model APIs are served from the resource root.
url = (override or os.environ.get("FOUNDRY_ENDPOINT", "")).strip().rstrip("/")
if not url:
raise ValueError("Set the FOUNDRY_ENDPOINT environment variable.")
return re.sub(r"/api/projects/[^/]+$", "", url)
def _to_tensor(img):
return torch.from_numpy(np.asarray(img.convert("RGB"), dtype=np.float32) / 255.0)[None, ...]
def _to_data_uri(image, index=0):
buf = io.BytesIO()
Image.fromarray((image[index].cpu().numpy() * 255).clip(0, 255).astype(np.uint8)).save(buf, "JPEG", quality=92)
return "data:image/jpeg;base64," + base64.b64encode(buf.getvalue()).decode()
def _video_to_data_uri(video):
buf = io.BytesIO()
video.save_to(buf, format="mp4") # keeps any trim applied in ComfyUI
return "data:video/mp4;base64," + base64.b64encode(buf.getvalue()).decode()
FLUX_PATHS = {"flux.2-pro": "flux-2-pro", "flux.2-flex": "flux-2-flex",
"flux.1-kontext-pro": "flux-kontext-pro", "flux-1.1-pro": "flux-pro-1.1"}
def _family(deployment, family):
name = deployment.lower()
if family != "auto":
return family
if "flux" in name:
return "flux"
if name.startswith(("mai-image", "mai_image")):
return "mai"
return "gpt-image"
class FoundryImageGenerate:
"""Text-to-image with a Foundry deployment: gpt-image, FLUX or MAI Image."""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"prompt": ("STRING", {"multiline": True, "default": ""}),
"deployment": ("STRING", {"default": "gpt-image-2.5-sunburst"}),
"size": (["1536x1024", "1024x1024", "1024x1536", "auto"], {"default": "1536x1024"}),
"quality": (["high", "medium", "low", "auto"], {"default": "high"}),
"seed": ("INT", {"default": 0, "min": 0, "max": 2**31 - 1}),
},
"optional": {
"model_family": (["auto", "gpt-image", "flux", "mai"], {"default": "auto"}),
"endpoint": ("STRING", {"default": "", "tooltip": "Empty = FOUNDRY_ENDPOINT"}),
},
}
RETURN_TYPES = ("IMAGE",)
FUNCTION = "generate"
CATEGORY = "Microsoft Foundry"
def generate(self, prompt, deployment, size, quality, seed, model_family="auto", endpoint=""):
family, base = _family(deployment, model_family), _base_url(endpoint)
dims = {} if size == "auto" else dict(zip(("width", "height"), map(int, size.split("x"))))
if family == "flux":
path = FLUX_PATHS.get(deployment.lower(), "flux-2-pro")
url = f"{base}/providers/blackforestlabs/v1/{path}?api-version=preview"
body = {"model": deployment, "prompt": prompt, "seed": seed, "output_format": "png", **dims}
elif family == "mai":
url = f"{base}/mai/v1/images/generations"
body = {"model": deployment, "prompt": prompt, **dims}
else:
url = f"{base}/openai/v1/images/generations"
body = {"model": deployment, "prompt": prompt, "size": size, "quality": quality, "n": 1}
r = requests.post(url, json=body, headers=_auth(), timeout=300)
if not r.ok:
raise RuntimeError(f"{family} image generation failed ({r.status_code}): {r.text[:500]}")
raw = base64.b64decode(r.json()["data"][0]["b64_json"])
return (_to_tensor(Image.open(io.BytesIO(raw))),)
class FalH3Video:
"""fal MiniMax H3 Max on Foundry: image-to-video, or text-to-video when no image is connected.
Connect reference images and/or a reference video to use reference-to-video."""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"prompt": ("STRING", {"multiline": True, "default": ""}),
"deployment": ("STRING", {"default": "minimax-h3"}),
"duration": ("INT", {"default": 5, "min": 1, "max": 10}),
"resolution": (["768P", "480P", "1080P"], {"default": "768P"}),
},
"optional": {
"image": ("IMAGE",),
"reference_images": ("IMAGE", {"tooltip": "Image 1, Image 2, ... in batch order"}),
"reference_video": ("VIDEO", {"tooltip": "Video 1, 2-15 seconds"}),
"endpoint": ("STRING", {"default": "", "tooltip": "Empty = FOUNDRY_ENDPOINT"}),
},
}
RETURN_TYPES = ("VIDEO", "STRING") if VideoFromFile else ("STRING",)
RETURN_NAMES = ("video", "path") if VideoFromFile else ("path",)
FUNCTION = "generate"
OUTPUT_NODE = True
CATEGORY = "Microsoft Foundry"
def generate(self, prompt, deployment, duration, resolution, image=None, endpoint="",
reference_images=None, reference_video=None):
base = _base_url(endpoint) + "/providers/falai/v1/minimax/h3-max"
body = {"model": deployment, "prompt": prompt, "duration": duration,
"resolution": resolution, "prompt_expansion_mode": "disabled"}
route = "text-to-video"
if image is not None:
body["image_url"], route = _to_data_uri(image), "image-to-video"
if reference_images is not None:
body["reference_image_urls"] = [_to_data_uri(reference_images, i) for i in range(len(reference_images))]
route = "reference-to-video"
if reference_video is not None:
body["reference_video_urls"], route = [_video_to_data_uri(reference_video)], "reference-to-video"
r = requests.post(f"{base}/{route}", json=body, headers=_auth(), timeout=120)
if not r.ok:
raise RuntimeError(f"fal submit failed ({r.status_code}): {r.text[:500]}")
request_id = r.json()["request_id"]
deadline = time.time() + 900
while True: # fal jobs are async: poll until done
status = requests.get(f"{base}/requests/{request_id}/status", headers=_auth(), timeout=60).json()
if status.get("status") == "COMPLETED":
break
if status.get("status") in ("FAILED", "CANCELED", "CANCELLED", "ERROR") or time.time() > deadline:
raise RuntimeError(f"fal request {request_id} did not complete: {status}")
time.sleep(5)
result = requests.get(f"{base}/requests/{request_id}", headers=_auth(), timeout=60).json()
video = requests.get(result["video"]["url"], headers=_auth(), timeout=300)
video.raise_for_status()
filename = f"h3_{uuid.uuid4().hex[:8]}.mp4"
path = os.path.join(folder_paths.get_output_directory(), filename)
with open(path, "wb") as f:
f.write(video.content)
ui = {"images": [{"filename": filename, "subfolder": "", "type": "output"}], "animated": (True,)}
return {"ui": ui, "result": (VideoFromFile(path), path) if VideoFromFile else (path,)}
NODE_CLASS_MAPPINGS = {"FoundryImageGenerate": FoundryImageGenerate, "FalH3Video": FalH3Video}
NODE_DISPLAY_NAME_MAPPINGS = {"FoundryImageGenerate": "Foundry Image Generate",
"FalH3Video": "fal MiniMax H3 Max Video (Foundry)"}3.3 Python paketlerini kurun. 3.1’de açtığınız terminalden:
python -m pip install azure-identity requestsWindows portable sürümünde ComfyUI_windows_portable klasöründen şunu çalıştırın:
python_embeded\python.exe -m pip install azure-identity requests3.4 Node’ları Foundry projenize yönlendirin. Node’lar proje uç noktasını FOUNDRY_ENDPOINT ortam değişkeninden okuyor; böylece uç nokta paylaştığınız iş akışlarına girmiyor.
| ComfyUI nasıl çalışıyor? | FOUNDRY_ENDPOINT nasıl ayarlanır? |
|---|---|
| ComfyUI Desktop | Manage → Startup Args → Environment Variables → + Add Variable |
| Windows (portable ya da elle) | setx FOUNDRY_ENDPOINT "https://<foundry-resource>.services.ai.azure.com/api/projects/<project>", ardından yeni bir terminal açın |
| macOS / Linux (elle) | python main.py öncesinde aynı terminalde export FOUNDRY_ENDPOINT="..." |
| Linux systemd servisi | sudo systemctl edit comfyui, ardından [Service] altına Environment=FOUNDRY_ENDPOINT=https://… |
3.5 ComfyUI’ı yeniden başlatıp node’ları kontrol edin. ComfyUI özel node’ları yalnızca başlangıçta yüklüyor. Başlangıç kaydında “Import times for custom nodes” altında ComfyUI-Foundry satırını arayın.

Satırda (IMPORT FAILED) yazıyorsa hatayı yukarıda arayın. “No module named ‘azure'” hatası paketlerin farklı bir Python’a kurulduğunu gösteriyor; 3.3’ü tablodaki tam Python yoluyla tekrarlayın. ComfyUI arayüzünü yenileyip Nodes panelini açtığınızda iki node Microsoft Foundry kategorisinde görünmeli.

Adım 4: Sinematik sahne hattını kurun
Görsel modeli sinematik bir anahtar kare üretiyor, video modeli de onu canlandırıyor. Tuvale üç node ekleyip şöyle bağlayın: Foundry Image Generate IMAGE → Save Image images ve Foundry Image Generate IMAGE → fal MiniMax H3 Max Video image.

| Node | Ayar | Değer |
|---|---|---|
| Foundry Image Generate | deployment / size / quality / seed | gpt-image-2.5-sunburst / 1536×1024 / high / 42 (sabit) |
| fal MiniMax H3 Max Video | deployment / duration / resolution | minimax-h3 / 5 / 768P |
Foundry Image Generate node’una çekimin görünümünü (lens, ışık ve renk düzeni) belirleyen bir anahtar kare prompt’u girin. Rehberdeki örnek:
Cinematic film still, shot on 35mm anamorphic lens. A lone lighthouse keeper in a weathered yellow raincoat stands on wet black rocks at dusk, facing a storm rolling in over the ocean. Huge waves crash and throw spray into the air, the lighthouse beam cuts through the rain behind him. Teal and orange color grade, volumetric light, shallow depth of field, subtle film grain, ultra realistic, 2.39:1 framing.fal video node’una ise yalnızca kamera hareketini ve aksiyonu anlatan bir hareket prompt’u girin; sahneyi anahtar kare zaten tanımlıyor:
Slow cinematic dolly-in toward the lighthouse keeper. The lighthouse beam sweeps through the rain, waves crash against the rocks and spray drifts across the frame, his raincoat flutters in the wind. Subtle handheld camera movement, moody film look, natural motion.Run’ı seçin. Anahtar kare birkaç saniyede geliyor, ardından fal node’u görselden videoya işi gönderip bitene kadar sorguluyor. Rehberdeki denemede tüm hat yaklaşık 80 saniye sürmüş.

Foundry görsel modelinden gelen anahtar kare (1536×1024):

fal MiniMax H3 Max’in ürettiği 5 saniyelik, 24 fps çekimden kareler:

Görsel modeli görünümü belirlediği, video modeli yalnızca hareket eklediği için çekim kareden kareye tutarlı kalıyor. İşi “anahtar kare → hareket” olarak bölmek, gerçekçi ve sanat yönetimi yapılmış bir sonuç almanın en kolay yolu.
İpuçları
- Görsel modelini değiştirin:
deploymentdeğerini gpt-image-2.5-sunburst, FLUX.2-pro ya da MAI-Image-2.6 yapın; node ailesi addan tanıyor. Dağıtımın özel bir adı varsamodel_family‘yi elle ayarlayın. MAI Image için iki kenarın da en az 768 piksel olması gerekiyor. - Başka bir Foundry kaynağındaki modeli kullanın: O kaynağın uç noktasını node’un isteğe bağlı
endpointalanına yapıştırın ve aynı kimliğe orada da aynı rolü verin. - Daha yüksek kalite: Son render için video node’unda çözünürlüğü 1080P yapın.
- Metinden video: fal node’unun image girdisini bağlamazsanız text-to-video çağrılıyor.
- Ucuz deneme: seed’i sabit tutun; ComfyUI girdisi değişmeyen node’ları önbelleğe aldığı için anahtar kareyi yeniden üretmeden hareket prompt’unu değiştirebilirsiniz.
Rehberin güçlü yanı, API anahtarı yerine Entra ID ve managed identity kullanması; paylaşılan ComfyUI iş akışlarında anahtar sızıntısı riskini ortadan kaldırıyor. fal modelleri şu an genel önizlemede ve ABD’de barındırılıyor; görsel ve video üretimi Foundry’de saniye ya da görsel başına ayrı faturalanıyor.


