<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Gonzalo Gjuka</title>
    <description>The latest articles on DEV Community by Gonzalo Gjuka (@gonzalogjuka).</description>
    <link>https://dev.to/gonzalogjuka</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4203133%2F9f9124f3-7c93-425f-a6dc-34fc421c7b7b.jpg</url>
      <title>DEV Community: Gonzalo Gjuka</title>
      <link>https://dev.to/gonzalogjuka</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/gonzalogjuka"/>
    <language>en</language>
    <item>
      <title>AllocationFailed, SkuNotAvailable e cota no Azure: qual é qual e qual se resolve tentando de novo</title>
      <dc:creator>Gonzalo Gjuka</dc:creator>
      <pubDate>Fri, 13 Nov 2026 23:33:43 +0000</pubDate>
      <link>https://dev.to/gonzalogjuka/allocationfailed-skunotavailable-e-cota-no-azure-qual-e-qual-e-qual-se-resolve-tentando-de-novo-1dei</link>
      <guid>https://dev.to/gonzalogjuka/allocationfailed-skunotavailable-e-cota-no-azure-qual-e-qual-e-qual-se-resolve-tentando-de-novo-1dei</guid>
      <description>&lt;p&gt;Um tópico do r/AZURE desta semana (40 comentários) procurava um banco de dados pequeno em West Europe e não achava nenhum tamanho disponível; nas respostas se misturavam capacidade, tamanhos em descontinuação e cota. O Azure devolve três erros diferentes que pelo portal parecem o mesmo: &lt;code&gt;AllocationFailed&lt;/code&gt;, &lt;code&gt;SkuNotAvailable&lt;/code&gt; e o de cota, &lt;code&gt;OperationNotAllowed&lt;/code&gt;. Cada um tem outra causa e outra correção, e só um pode ser resolvido tentando de novo. É o equivalente no Azure do "Out of host capacity" da OCI, que tem a sua &lt;a href="https://controlcloud.dev/pt/blog/oci-out-of-host-capacity-a1-flex-retry-loop?utm_source=devto&amp;amp;utm_medium=referral&amp;amp;utm_campaign=devto-azure-alloc-pt" rel="noopener noreferrer"&gt;nota com um loop de retentativas&lt;/a&gt;. Tudo o que segue sai de learn.microsoft.com; as fontes estão no final.&lt;/p&gt;

&lt;h2&gt;
  
  
  Os três erros
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;AllocationFailed&lt;/code&gt; ou &lt;code&gt;ZonalAllocationFailed&lt;/code&gt;: "Allocation failed. We do not have sufficient capacity for the requested VM size in this region". É capacidade: naquele momento não há hosts livres para esse tamanho na região ou na zona. Aparece ao criar, ao iniciar uma VM desalocada (deallocated) ou ao mudar o tamanho dela. É o único que pode ser resolvido tentando de novo.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;SkuNotAvailable&lt;/code&gt;: "The requested size for resource … is currently not available in location … zones … for subscription …". O tamanho não é oferecido à sua assinatura nessa região ou zona. Com um template ARM ou Bicep chega como &lt;code&gt;InvalidTemplateDeployment&lt;/code&gt; na validação: o deployment não começa e o erro fica só no activity log. Tentar de novo não muda nada.&lt;/li&gt;
&lt;li&gt;Cota: &lt;code&gt;OperationNotAllowed&lt;/code&gt; com "Operation results in exceeding quota limits of Core. Maximum allowed: 4, Current in use: 4, Additional requested: 2". É um limite da sua assinatura, não falta de hardware: resolve-se pedindo mais cota ou liberando vCPUs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A Microsoft diz isso na página de cotas: o Azure verifica cota e capacidade separadamente. Ter cota de sobra não garante capacidade, e haver capacidade não adianta se a cota estiver cheia.&lt;/p&gt;

&lt;h2&gt;
  
  
  SkuNotAvailable: ler a restrição em vez de adivinhar
&lt;/h2&gt;

&lt;p&gt;O portal só mostra os tamanhos que passam; não diz por que falta o resto. &lt;code&gt;az vm list-skus&lt;/code&gt; com &lt;code&gt;--all&lt;/code&gt; inclui os que não estão disponíveis para a sua assinatura e mostra a restrição:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# tamanhos da família B na região, incluindo os restritos&lt;/span&gt;
az vm list-skus --location westeurope --size Standard_B --all --output table

&lt;span class="c"&gt;# o objeto de restrição de um tamanho específico&lt;/span&gt;
az vm list-skus --location westeurope --size Standard_B2s --all \
  --query &lt;span class="s2"&gt;"[?name=='Standard_B2s'].restrictions"&lt;/span&gt;

&lt;span class="c"&gt;# só VMs, com o detalhe por zona&lt;/span&gt;
az vm list-skus --location westeurope --resource-type virtualMachines --zone --all --output table
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A coluna Restrictions (em JSON, o campo &lt;code&gt;restrictions&lt;/code&gt; que a API Resource Skus devolve) traz os dois dados que decidem o que fazer:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;type&lt;/code&gt;: &lt;code&gt;Location&lt;/code&gt; se o tamanho está restrito em toda a região, &lt;code&gt;Zone&lt;/code&gt; se só em algumas zonas. Nesse caso &lt;code&gt;restrictionInfo.zones&lt;/code&gt; lista as zonas fechadas, e as outras podem aceitar o tamanho.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;reasonCode&lt;/code&gt;: no caso que a documentação mostra, &lt;code&gt;NotAvailableForSubscription&lt;/code&gt;: a região não oferece esse tamanho à sua assinatura.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Se o tamanho está restrito em toda a região e você precisa dele ali, a Microsoft indica pedi-lo ao suporte com um SKU request. Se está restrito só em uma zona, a saída é outra zona ou tirar a restrição de zona. As VMs Spot também dão &lt;code&gt;SkuNotAvailable&lt;/code&gt; quando não há capacidade Spot na região. Para o Azure Database for PostgreSQL Flexible Server, o equivalente é &lt;code&gt;az postgres flexible-server list-skus --location &amp;lt;região&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cota: dois níveis por região, e as VMs paradas também contam
&lt;/h2&gt;

&lt;p&gt;A cota de vCPUs tem dois níveis por assinatura e por região: Total Regional vCPUs e uma cota por família (por exemplo, Standard DSv3 Family vCPUs), além de um teto de quantidade de VMs. Uma VM nova tem que caber nas duas cotas de vCPU. Um detalhe que confunde: a cota é calculada com os cores em uso, alocados e desalocados, então uma VM parada (deallocated) continua ocupando cota mesmo sem cobrar compute.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# uso e limite de cada cota de compute na região&lt;/span&gt;
az vm list-usage --location westeurope --output table
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Resolve-se com um pedido de aumento (Subscription → Usage + quotas) ou apagando as VMs que ninguém usa; desalocá-las não basta.&lt;/p&gt;

&lt;h2&gt;
  
  
  AllocationFailed: o que mudar em cada caso
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;VM avulsa (sem availability set nem proximity placement group): tentar de novo mais tarde, outro tamanho ou outra região ou zona. A Microsoft apresenta isso como solução temporária até haver capacidade.&lt;/li&gt;
&lt;li&gt;Availability set: adicionar uma VM, mudar o tamanho ou iniciar só parte das VMs desalocadas é pedido ao cluster original do set, que pode não ter espaço. Parar (deallocate) todas as VMs do set e iniciá-las juntas deixa a alocação procurar em todos os clusters.&lt;/li&gt;
&lt;li&gt;Zonas: criar a VM a partir de um snapshot do disco do sistema em outra zona, ou sem restrição de zona, abre a alocação para toda a região.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;OverconstrainedAllocationRequest&lt;/code&gt; ou &lt;code&gt;OverconstrainedZonalAllocationRequest&lt;/code&gt;: a combinação de requisitos não cabe. A Microsoft cita accelerated networking, zona, disco efêmero, proximity placement group e discos Ultra ou Premium SSD v2; tirar um deles pode bastar mesmo havendo capacidade do tamanho.&lt;/li&gt;
&lt;li&gt;Tamanhos antigos (Av1, Dv1, DSv1, D15v2, DS15v2): não rodam no hardware novo e falham com mais frequência. A recomendação é passar para Av2, Dv3/DSv3 ou Ev3/ESv3.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Se a capacidade tem que estar lá de qualquer jeito, existe a On-demand Capacity Reservation: reserva o espaço com antecedência, mas é cobrada pela tarifa da VM mesmo quando a reserva não é usada.&lt;/p&gt;

&lt;h2&gt;
  
  
  Uma retentativa que não perde horas no que não se resolve
&lt;/h2&gt;

&lt;p&gt;Antes de deixar um loop tentando de novo, vale descartar os dois erros que não mudam com o tempo: &lt;code&gt;list-usage&lt;/code&gt; para ver se é cota e &lt;code&gt;list-skus --all&lt;/code&gt; para ver se o tamanho está restrito. Só então faz sentido tentar de novo, alternando tamanhos da mesma família. Este exemplo testa três tamanhos em uma VM existente e pula os que aparecem como restritos; &lt;code&gt;az vm resize&lt;/code&gt; reinicia a VM se ela estiver rodando:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;RG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;meu-rg&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;VM&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;minha-vm&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;LOC&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;westeurope
&lt;span class="k"&gt;for&lt;/span&gt; size &lt;span class="k"&gt;in&lt;/span&gt; Standard_D2as_v5 Standard_D2s_v5 Standard_D2ads_v5&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="nv"&gt;r&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$(&lt;/span&gt;az vm list-skus -l &lt;span class="nv"&gt;$LOC&lt;/span&gt; --size &lt;span class="nv"&gt;$size&lt;/span&gt; --all \
    --query &lt;span class="s2"&gt;"[?name=='$size'].restrictions[0].type"&lt;/span&gt; -o tsv&lt;span class="o"&gt;)&lt;/span&gt;
  &lt;span class="o"&gt;[&lt;/span&gt; -n &lt;span class="s2"&gt;"$r"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"$size restrito ($r), próximo"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;continue&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
  az vm resize -g &lt;span class="nv"&gt;$RG&lt;/span&gt; -n &lt;span class="nv"&gt;$VM&lt;/span&gt; --size &lt;span class="nv"&gt;$size&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;break&lt;/span&gt;
&lt;span class="k"&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  O que o Control Cloud faz com esses erros
&lt;/h2&gt;

&lt;p&gt;Quando um deploy falha, o decodificador de erros do Control Cloud traduz &lt;code&gt;SkuNotAvailable&lt;/code&gt; e &lt;code&gt;QuotaExceeded&lt;/code&gt; em uma explicação com o comando somente leitura para confirmá-lo (&lt;code&gt;az vm list-skus&lt;/code&gt; e &lt;code&gt;az vm list-usage&lt;/code&gt;). Ainda não reconhece &lt;code&gt;AllocationFailed&lt;/code&gt; nem o &lt;code&gt;OperationNotAllowed&lt;/code&gt; de cota de cores, e o pre-flight não consulta cota nem SKUs do Azure antes de implantar: hoje é para isso que servem os comandos desta nota. Para conectar uma assinatura com permissões de leitura, há o &lt;a href="https://controlcloud.dev/pt/guias/azure?utm_source=devto&amp;amp;utm_medium=referral&amp;amp;utm_campaign=devto-azure-alloc-pt" rel="noopener noreferrer"&gt;guia do Azure&lt;/a&gt; e a nota do &lt;a href="https://controlcloud.dev/pt/blog/service-principal-azure-solo-lectura-reader?utm_source=devto&amp;amp;utm_medium=referral&amp;amp;utm_campaign=devto-azure-alloc-pt" rel="noopener noreferrer"&gt;service principal somente leitura&lt;/a&gt;. Se quiser testar, &lt;a href="https://controlcloud.dev/pt/contact?utm_source=devto&amp;amp;utm_medium=referral&amp;amp;utm_campaign=devto-azure-alloc-pt" rel="noopener noreferrer"&gt;peça acesso&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fontes
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Microsoft Learn, "Troubleshooting Azure VM allocation failures" (AllocationFailed, ZonalAllocationFailed, availability sets, zonas, Overconstrained e tamanhos antigos).&lt;/li&gt;
&lt;li&gt;Microsoft Learn, "SKU not available errors" (SkuNotAvailable, InvalidTemplateDeployment, &lt;code&gt;az vm list-skus --all&lt;/code&gt;, Spot).&lt;/li&gt;
&lt;li&gt;Microsoft Learn, referência da API "Resource Skus - List" (restrictions: type Location ou Zone, reasonCode, restrictionInfo.zones).&lt;/li&gt;
&lt;li&gt;Microsoft Learn, "vCPU quotas" (dois níveis, cota e capacidade verificadas separadamente, cores alocados e desalocados, &lt;code&gt;az vm list-usage&lt;/code&gt;) e "Resource quota errors" (OperationNotAllowed).&lt;/li&gt;
&lt;li&gt;Microsoft Learn, "On-demand Capacity Reservation" (cobrada pela tarifa da VM mesmo sem uso) e a referência de &lt;code&gt;az postgres flexible-server list-skus&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Neste repositório: o dicionário de erros do backend (errordict.go).&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>braziliandevs</category>
      <category>azure</category>
      <category>devops</category>
      <category>cloud</category>
    </item>
    <item>
      <title>AllocationFailed, SkuNotAvailable y cuota en Azure: cuál es cuál y cuál se arregla reintentando</title>
      <dc:creator>Gonzalo Gjuka</dc:creator>
      <pubDate>Fri, 13 Nov 2026 23:33:33 +0000</pubDate>
      <link>https://dev.to/gonzalogjuka/allocationfailed-skunotavailable-y-cuota-en-azure-cual-es-cual-y-cual-se-arregla-reintentando-2odg</link>
      <guid>https://dev.to/gonzalogjuka/allocationfailed-skunotavailable-y-cuota-en-azure-cual-es-cual-y-cual-se-arregla-reintentando-2odg</guid>
      <description>&lt;p&gt;Un hilo de r/AZURE de esta semana (40 comentarios) buscaba una base de datos chica en West Europe y no encontraba ningún tamaño disponible; en las respuestas se mezclaban capacidad, tamaños en retiro y cuota. Azure devuelve tres errores distintos que desde el portal parecen el mismo: &lt;code&gt;AllocationFailed&lt;/code&gt;, &lt;code&gt;SkuNotAvailable&lt;/code&gt; y el de cuota, &lt;code&gt;OperationNotAllowed&lt;/code&gt;. Cada uno tiene otra causa y otro arreglo, y sólo uno puede resolverse reintentando. Es el equivalente en Azure del "Out of host capacity" de OCI, que tiene su &lt;a href="https://controlcloud.dev/blog/oci-out-of-host-capacity-a1-flex-retry-loop?utm_source=devto&amp;amp;utm_medium=referral&amp;amp;utm_campaign=devto-azure-alloc-es" rel="noopener noreferrer"&gt;nota con un loop de reintentos&lt;/a&gt;. Todo lo que sigue sale de learn.microsoft.com; las fuentes están al final.&lt;/p&gt;

&lt;h2&gt;
  
  
  Los tres errores
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;AllocationFailed&lt;/code&gt; o &lt;code&gt;ZonalAllocationFailed&lt;/code&gt;: "Allocation failed. We do not have sufficient capacity for the requested VM size in this region". Es capacidad: en ese momento no hay hosts libres para ese tamaño en la región o la zona. Aparece al crear, al arrancar una VM desasignada (deallocated) o al cambiarle el tamaño. Es el único que puede resolverse reintentando.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;SkuNotAvailable&lt;/code&gt;: "The requested size for resource … is currently not available in location … zones … for subscription …". El tamaño no se le ofrece a tu suscripción en esa región o zona. Con una plantilla ARM o Bicep llega como &lt;code&gt;InvalidTemplateDeployment&lt;/code&gt; en la validación: el deployment no arranca y el error queda sólo en el activity log. Reintentar no cambia nada.&lt;/li&gt;
&lt;li&gt;Cuota: &lt;code&gt;OperationNotAllowed&lt;/code&gt; con "Operation results in exceeding quota limits of Core. Maximum allowed: 4, Current in use: 4, Additional requested: 2". Es un límite de tu suscripción, no falta de hardware: se arregla pidiendo más cuota o liberando vCPUs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Microsoft lo dice en su página de cuotas: Azure chequea cuota y capacidad por separado. Tener cuota de sobra no garantiza capacidad, y que haya capacidad no sirve si la cuota está llena.&lt;/p&gt;

&lt;h2&gt;
  
  
  SkuNotAvailable: leer la restricción en vez de adivinar
&lt;/h2&gt;

&lt;p&gt;El portal sólo muestra los tamaños que pasan; no dice por qué falta el resto. &lt;code&gt;az vm list-skus&lt;/code&gt; con &lt;code&gt;--all&lt;/code&gt; incluye los que no están disponibles para tu suscripción y muestra la restricción:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# tamaños de la familia B en la región, incluidos los restringidos&lt;/span&gt;
az vm list-skus --location westeurope --size Standard_B --all --output table

&lt;span class="c"&gt;# el objeto de restricción de un tamaño puntual&lt;/span&gt;
az vm list-skus --location westeurope --size Standard_B2s --all \
  --query &lt;span class="s2"&gt;"[?name=='Standard_B2s'].restrictions"&lt;/span&gt;

&lt;span class="c"&gt;# sólo VMs, con el detalle por zona&lt;/span&gt;
az vm list-skus --location westeurope --resource-type virtualMachines --zone --all --output table
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;La columna Restrictions (en JSON, el campo &lt;code&gt;restrictions&lt;/code&gt; que devuelve la API Resource Skus) trae los dos datos que deciden qué hacer:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;type&lt;/code&gt;: &lt;code&gt;Location&lt;/code&gt; si el tamaño está restringido en toda la región, &lt;code&gt;Zone&lt;/code&gt; si sólo en algunas zonas. En ese caso &lt;code&gt;restrictionInfo.zones&lt;/code&gt; lista las zonas cerradas, y las otras pueden aceptar el tamaño.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;reasonCode&lt;/code&gt;: en el caso que muestra la documentación, &lt;code&gt;NotAvailableForSubscription&lt;/code&gt;: la región no le ofrece ese tamaño a tu suscripción.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Si el tamaño está restringido en toda la región y lo necesitás ahí, Microsoft indica pedirlo a soporte con un SKU request. Si sólo está restringido en una zona, la salida es otra zona o sacar la restricción de zona. Las VMs Spot también dan &lt;code&gt;SkuNotAvailable&lt;/code&gt; cuando no hay capacidad Spot en la región. Para Azure Database for PostgreSQL Flexible Server, el equivalente es &lt;code&gt;az postgres flexible-server list-skus --location &amp;lt;región&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cuota: dos niveles por región, y las VMs apagadas también cuentan
&lt;/h2&gt;

&lt;p&gt;La cuota de vCPUs tiene dos niveles por suscripción y por región: Total Regional vCPUs y una cuota por familia (por ejemplo, Standard DSv3 Family vCPUs), además de un tope de cantidad de VMs. Una VM nueva tiene que entrar en las dos cuotas de vCPU. Un detalle que confunde: la cuota se calcula con los cores en uso, asignados y desasignados, así que una VM detenida (deallocated) sigue ocupando cuota aunque no cobre cómputo.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# uso y límite de cada cuota de cómputo en la región&lt;/span&gt;
az vm list-usage --location westeurope --output table
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Se arregla con un pedido de aumento (Subscription → Usage + quotas) o borrando las VMs que no se usan; desasignarlas no alcanza.&lt;/p&gt;

&lt;h2&gt;
  
  
  AllocationFailed: qué cambiar según el caso
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;VM suelta (sin availability set ni proximity placement group): reintentar más tarde, otro tamaño u otra región o zona. Microsoft lo presenta como solución temporal hasta que haya capacidad.&lt;/li&gt;
&lt;li&gt;Availability set: agregar una VM, cambiar el tamaño o arrancar sólo parte de las VMs desasignadas se le pide al cluster original del set, que puede no tener lugar. Detener (deallocate) todas las VMs del set y arrancarlas juntas deja que la asignación busque en todos los clusters.&lt;/li&gt;
&lt;li&gt;Zonas: crear la VM desde un snapshot del disco del sistema en otra zona, o sin restricción de zona, abre la asignación a toda la región.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;OverconstrainedAllocationRequest&lt;/code&gt; u &lt;code&gt;OverconstrainedZonalAllocationRequest&lt;/code&gt;: la combinación de requisitos no entra. Microsoft nombra accelerated networking, zona, disco efímero, proximity placement group y discos Ultra o Premium SSD v2; sacar uno puede alcanzar aunque haya capacidad del tamaño.&lt;/li&gt;
&lt;li&gt;Tamaños viejos (Av1, Dv1, DSv1, D15v2, DS15v2): no corren en el hardware nuevo y fallan más seguido. La recomendación es pasar a Av2, Dv3/DSv3 o Ev3/ESv3.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Si la capacidad tiene que estar sí o sí, existe la On-demand Capacity Reservation: reserva el lugar por adelantado, pero se cobra a la tarifa de la VM aunque la reserva no se use.&lt;/p&gt;

&lt;h2&gt;
  
  
  Un reintento que no pierde horas en lo que no se arregla
&lt;/h2&gt;

&lt;p&gt;Antes de dejar un loop reintentando, conviene descartar los dos errores que no cambian con el tiempo: &lt;code&gt;list-usage&lt;/code&gt; para ver si es cuota y &lt;code&gt;list-skus --all&lt;/code&gt; para ver si el tamaño está restringido. Recién ahí tiene sentido reintentar, rotando tamaños de la misma familia. Este ejemplo prueba tres tamaños sobre una VM existente y saltea los que figuran restringidos; &lt;code&gt;az vm resize&lt;/code&gt; reinicia la VM si está corriendo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;RG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;mi-rg&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;VM&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;mi-vm&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;LOC&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;westeurope
&lt;span class="k"&gt;for&lt;/span&gt; size &lt;span class="k"&gt;in&lt;/span&gt; Standard_D2as_v5 Standard_D2s_v5 Standard_D2ads_v5&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="nv"&gt;r&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$(&lt;/span&gt;az vm list-skus -l &lt;span class="nv"&gt;$LOC&lt;/span&gt; --size &lt;span class="nv"&gt;$size&lt;/span&gt; --all \
    --query &lt;span class="s2"&gt;"[?name=='$size'].restrictions[0].type"&lt;/span&gt; -o tsv&lt;span class="o"&gt;)&lt;/span&gt;
  &lt;span class="o"&gt;[&lt;/span&gt; -n &lt;span class="s2"&gt;"$r"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"$size restringido ($r), sigo"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;continue&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
  az vm resize -g &lt;span class="nv"&gt;$RG&lt;/span&gt; -n &lt;span class="nv"&gt;$VM&lt;/span&gt; --size &lt;span class="nv"&gt;$size&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;break&lt;/span&gt;
&lt;span class="k"&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Lo que hace Control Cloud con estos errores
&lt;/h2&gt;

&lt;p&gt;Cuando un deploy falla, el decodificador de errores de Control Cloud traduce &lt;code&gt;SkuNotAvailable&lt;/code&gt; y &lt;code&gt;QuotaExceeded&lt;/code&gt; a una explicación con el comando de solo lectura para confirmarlo (&lt;code&gt;az vm list-skus&lt;/code&gt; y &lt;code&gt;az vm list-usage&lt;/code&gt;). Todavía no reconoce &lt;code&gt;AllocationFailed&lt;/code&gt; ni el &lt;code&gt;OperationNotAllowed&lt;/code&gt; de cuota de cores, y el pre-flight no consulta cuota ni SKUs de Azure antes de desplegar: hoy eso son los comandos de esta nota. Para conectar una suscripción con permisos de lectura están la &lt;a href="https://controlcloud.dev/guias/azure?utm_source=devto&amp;amp;utm_medium=referral&amp;amp;utm_campaign=devto-azure-alloc-es" rel="noopener noreferrer"&gt;guía de Azure&lt;/a&gt; y la nota del &lt;a href="https://controlcloud.dev/blog/service-principal-azure-solo-lectura-reader?utm_source=devto&amp;amp;utm_medium=referral&amp;amp;utm_campaign=devto-azure-alloc-es" rel="noopener noreferrer"&gt;service principal de solo lectura&lt;/a&gt;. Si querés probarlo, &lt;a href="https://controlcloud.dev/contact?utm_source=devto&amp;amp;utm_medium=referral&amp;amp;utm_campaign=devto-azure-alloc-es" rel="noopener noreferrer"&gt;pedí acceso&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fuentes
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Microsoft Learn, "Troubleshooting Azure VM allocation failures" (AllocationFailed, ZonalAllocationFailed, availability sets, zonas, Overconstrained y tamaños viejos).&lt;/li&gt;
&lt;li&gt;Microsoft Learn, "SKU not available errors" (SkuNotAvailable, InvalidTemplateDeployment, &lt;code&gt;az vm list-skus --all&lt;/code&gt;, Spot).&lt;/li&gt;
&lt;li&gt;Microsoft Learn, referencia de la API "Resource Skus - List" (restrictions: type Location o Zone, reasonCode, restrictionInfo.zones).&lt;/li&gt;
&lt;li&gt;Microsoft Learn, "vCPU quotas" (dos niveles, cuota y capacidad por separado, cores asignados y desasignados, &lt;code&gt;az vm list-usage&lt;/code&gt;) y "Resource quota errors" (OperationNotAllowed).&lt;/li&gt;
&lt;li&gt;Microsoft Learn, "On-demand Capacity Reservation" (se cobra a la tarifa de la VM aunque no se use) y la referencia de &lt;code&gt;az postgres flexible-server list-skus&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;En este repositorio: el diccionario de errores del backend (errordict.go).&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>azure</category>
      <category>devops</category>
      <category>cloud</category>
      <category>sre</category>
    </item>
    <item>
      <title>Azure AllocationFailed, SkuNotAvailable and quota errors: which is which, and which one a retry fixes</title>
      <dc:creator>Gonzalo Gjuka</dc:creator>
      <pubDate>Fri, 13 Nov 2026 23:33:01 +0000</pubDate>
      <link>https://dev.to/gonzalogjuka/azure-allocationfailed-skunotavailable-and-quota-errors-which-is-which-and-which-one-a-retry-5dl2</link>
      <guid>https://dev.to/gonzalogjuka/azure-allocationfailed-skunotavailable-and-quota-errors-which-is-which-and-which-one-a-retry-5dl2</guid>
      <description>&lt;p&gt;An r/AZURE thread this week (40 comments) was looking for a small database in West Europe and could not find a single available size; the replies mixed capacity, retiring sizes and quota. Azure returns three different errors that look the same from the portal: &lt;code&gt;AllocationFailed&lt;/code&gt;, &lt;code&gt;SkuNotAvailable&lt;/code&gt; and the quota one, &lt;code&gt;OperationNotAllowed&lt;/code&gt;. Each has a different cause and a different fix, and only one of them can be solved by retrying. It is Azure's counterpart to OCI's "Out of host capacity", which has its own &lt;a href="https://controlcloud.dev/en/blog/oci-out-of-host-capacity-a1-flex-retry-loop?utm_source=devto&amp;amp;utm_medium=referral&amp;amp;utm_campaign=devto-azure-alloc-en" rel="noopener noreferrer"&gt;note with a retry loop&lt;/a&gt;. Everything below comes from learn.microsoft.com; the sources are at the end.&lt;/p&gt;

&lt;h2&gt;
  
  
  The three errors
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;AllocationFailed&lt;/code&gt; or &lt;code&gt;ZonalAllocationFailed&lt;/code&gt;: "Allocation failed. We do not have sufficient capacity for the requested VM size in this region". This is capacity: right now there are no free hosts for that size in the region or zone. It shows up on create, on starting a deallocated VM or on resizing one. It is the only one a retry can solve.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;SkuNotAvailable&lt;/code&gt;: "The requested size for resource … is currently not available in location … zones … for subscription …". The size is not offered to your subscription in that region or zone. With an ARM template or Bicep file it arrives as &lt;code&gt;InvalidTemplateDeployment&lt;/code&gt; during validation: the deployment never starts and the error is only in the activity log. Retrying changes nothing.&lt;/li&gt;
&lt;li&gt;Quota: &lt;code&gt;OperationNotAllowed&lt;/code&gt; with "Operation results in exceeding quota limits of Core. Maximum allowed: 4, Current in use: 4, Additional requested: 2". It is a limit on your subscription, not missing hardware: you fix it by requesting more quota or freeing vCPUs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Microsoft says it on its quotas page: Azure checks quota and capacity separately. Plenty of quota does not guarantee capacity, and capacity does not help if the quota is full.&lt;/p&gt;

&lt;h2&gt;
  
  
  SkuNotAvailable: read the restriction instead of guessing
&lt;/h2&gt;

&lt;p&gt;The portal only shows the sizes that pass; it never says why the rest are missing. &lt;code&gt;az vm list-skus&lt;/code&gt; with &lt;code&gt;--all&lt;/code&gt; includes the ones not available to your subscription and prints the restriction:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# B-family sizes in the region, restricted ones included&lt;/span&gt;
az vm list-skus --location westeurope --size Standard_B --all --output table

&lt;span class="c"&gt;# the restriction object for one size&lt;/span&gt;
az vm list-skus --location westeurope --size Standard_B2s --all \
  --query &lt;span class="s2"&gt;"[?name=='Standard_B2s'].restrictions"&lt;/span&gt;

&lt;span class="c"&gt;# VMs only, with per-zone detail&lt;/span&gt;
az vm list-skus --location westeurope --resource-type virtualMachines --zone --all --output table
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Restrictions column (in JSON, the &lt;code&gt;restrictions&lt;/code&gt; field the Resource Skus API returns) carries the two values that decide what to do:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;type&lt;/code&gt;: &lt;code&gt;Location&lt;/code&gt; if the size is restricted across the whole region, &lt;code&gt;Zone&lt;/code&gt; if only in some zones. In that case &lt;code&gt;restrictionInfo.zones&lt;/code&gt; lists the closed zones, and the others can still take the size.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;reasonCode&lt;/code&gt;: in the case the documentation shows, &lt;code&gt;NotAvailableForSubscription&lt;/code&gt;: the region does not offer that size to your subscription.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If the size is restricted across the region and you need it there, Microsoft says to ask Support with a SKU request. If it is only restricted in one zone, the way out is another zone or dropping the zone constraint. Spot VMs also return &lt;code&gt;SkuNotAvailable&lt;/code&gt; when there is no Spot capacity in the region. For Azure Database for PostgreSQL Flexible Server, the equivalent is &lt;code&gt;az postgres flexible-server list-skus --location &amp;lt;region&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quota: two tiers per region, and stopped VMs count too
&lt;/h2&gt;

&lt;p&gt;vCPU quota has two tiers per subscription and per region: Total Regional vCPUs and a per-family quota (for example, Standard DSv3 Family vCPUs), plus a cap on the number of VMs. A new VM has to fit in both vCPU quotas. One detail that trips people up: quota is calculated from cores in use, allocated and deallocated, so a stopped (deallocated) VM keeps holding quota even though it is not billed for compute.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# usage and limit of every compute quota in the region&lt;/span&gt;
az vm list-usage --location westeurope --output table
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The fix is an increase request (Subscription → Usage + quotas) or deleting the VMs nobody uses; deallocating them is not enough.&lt;/p&gt;

&lt;h2&gt;
  
  
  AllocationFailed: what to change for each case
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Standalone VM (no availability set or proximity placement group): retry later, another size, or another region or zone. Microsoft presents it as a temporary workaround until capacity is back.&lt;/li&gt;
&lt;li&gt;Availability set: adding a VM, resizing, or starting only some of the deallocated VMs is requested from the set's original cluster, which may have no room. Stopping (deallocating) every VM in the set and starting them together lets the allocation search all clusters.&lt;/li&gt;
&lt;li&gt;Zones: creating the VM from a snapshot of the OS disk in another zone, or without a zone constraint, opens the allocation to the whole region.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;OverconstrainedAllocationRequest&lt;/code&gt; or &lt;code&gt;OverconstrainedZonalAllocationRequest&lt;/code&gt;: the combination of requirements does not fit. Microsoft names accelerated networking, zone, ephemeral disk, proximity placement group and Ultra or Premium SSD v2 disks; dropping one can be enough even when the size has capacity.&lt;/li&gt;
&lt;li&gt;Older sizes (Av1, Dv1, DSv1, D15v2, DS15v2): they do not run on the newest hardware and fail more often. The recommendation is to move to Av2, Dv3/DSv3 or Ev3/ESv3.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If the capacity must be there no matter what, there is On-demand Capacity Reservation: it holds the room in advance, but it is billed at the VM's rate even when the reservation is unused.&lt;/p&gt;

&lt;h2&gt;
  
  
  A retry that does not burn hours on what will not change
&lt;/h2&gt;

&lt;p&gt;Before leaving a loop retrying, rule out the two errors that do not change over time: &lt;code&gt;list-usage&lt;/code&gt; to see whether it is quota and &lt;code&gt;list-skus --all&lt;/code&gt; to see whether the size is restricted. Only then does a retry make sense, rotating sizes in the same family. This example tries three sizes on an existing VM and skips the ones listed as restricted; &lt;code&gt;az vm resize&lt;/code&gt; restarts the VM if it is running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;RG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;my-rg&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;VM&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;my-vm&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;LOC&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;westeurope
&lt;span class="k"&gt;for&lt;/span&gt; size &lt;span class="k"&gt;in&lt;/span&gt; Standard_D2as_v5 Standard_D2s_v5 Standard_D2ads_v5&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="nv"&gt;r&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$(&lt;/span&gt;az vm list-skus -l &lt;span class="nv"&gt;$LOC&lt;/span&gt; --size &lt;span class="nv"&gt;$size&lt;/span&gt; --all \
    --query &lt;span class="s2"&gt;"[?name=='$size'].restrictions[0].type"&lt;/span&gt; -o tsv&lt;span class="o"&gt;)&lt;/span&gt;
  &lt;span class="o"&gt;[&lt;/span&gt; -n &lt;span class="s2"&gt;"$r"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"$size restricted ($r), next"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;continue&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
  az vm resize -g &lt;span class="nv"&gt;$RG&lt;/span&gt; -n &lt;span class="nv"&gt;$VM&lt;/span&gt; --size &lt;span class="nv"&gt;$size&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;break&lt;/span&gt;
&lt;span class="k"&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What Control Cloud does with these errors
&lt;/h2&gt;

&lt;p&gt;When a deploy fails, Control Cloud's error decoder turns &lt;code&gt;SkuNotAvailable&lt;/code&gt; and &lt;code&gt;QuotaExceeded&lt;/code&gt; into an explanation with the read-only command that confirms it (&lt;code&gt;az vm list-skus&lt;/code&gt; and &lt;code&gt;az vm list-usage&lt;/code&gt;). It does not recognize &lt;code&gt;AllocationFailed&lt;/code&gt; or the core-quota &lt;code&gt;OperationNotAllowed&lt;/code&gt; yet, and the pre-flight does not query Azure quota or SKUs before deploying: today that is what the commands in this note are for. To connect a subscription with read permissions, see the &lt;a href="https://controlcloud.dev/en/guias/azure?utm_source=devto&amp;amp;utm_medium=referral&amp;amp;utm_campaign=devto-azure-alloc-en" rel="noopener noreferrer"&gt;Azure guide&lt;/a&gt; and the note on the &lt;a href="https://controlcloud.dev/en/blog/service-principal-azure-solo-lectura-reader?utm_source=devto&amp;amp;utm_medium=referral&amp;amp;utm_campaign=devto-azure-alloc-en" rel="noopener noreferrer"&gt;read-only service principal&lt;/a&gt;. If you want to try it, &lt;a href="https://controlcloud.dev/en/contact?utm_source=devto&amp;amp;utm_medium=referral&amp;amp;utm_campaign=devto-azure-alloc-en" rel="noopener noreferrer"&gt;request access&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Microsoft Learn, "Troubleshooting Azure VM allocation failures" (AllocationFailed, ZonalAllocationFailed, availability sets, zones, Overconstrained and older sizes).&lt;/li&gt;
&lt;li&gt;Microsoft Learn, "SKU not available errors" (SkuNotAvailable, InvalidTemplateDeployment, &lt;code&gt;az vm list-skus --all&lt;/code&gt;, Spot).&lt;/li&gt;
&lt;li&gt;Microsoft Learn, "Resource Skus - List" API reference (restrictions: type Location or Zone, reasonCode, restrictionInfo.zones).&lt;/li&gt;
&lt;li&gt;Microsoft Learn, "vCPU quotas" (two tiers, quota and capacity checked separately, allocated and deallocated cores, &lt;code&gt;az vm list-usage&lt;/code&gt;) and "Resource quota errors" (OperationNotAllowed).&lt;/li&gt;
&lt;li&gt;Microsoft Learn, "On-demand Capacity Reservation" (billed at the VM rate even when unused) and the &lt;code&gt;az postgres flexible-server list-skus&lt;/code&gt; reference.&lt;/li&gt;
&lt;li&gt;In this repository: the backend error dictionary (errordict.go).&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>azure</category>
      <category>devops</category>
      <category>cloud</category>
      <category>sre</category>
    </item>
    <item>
      <title>OCID do usuário na OCI: onde ficam o user OCID, o tenancy OCID, o compartment e a fingerprint, e por que a CLI responde 401</title>
      <dc:creator>Gonzalo Gjuka</dc:creator>
      <pubDate>Fri, 13 Nov 2026 12:28:05 +0000</pubDate>
      <link>https://dev.to/gonzalogjuka/ocid-do-usuario-na-oci-onde-ficam-o-user-ocid-o-tenancy-ocid-o-compartment-e-a-fingerprint-e-5g6n</link>
      <guid>https://dev.to/gonzalogjuka/ocid-do-usuario-na-oci-onde-ficam-o-user-ocid-o-tenancy-ocid-o-compartment-e-a-fingerprint-e-5g6n</guid>
      <description>&lt;p&gt;O &lt;a href="https://controlcloud.dev/pt/guias/oci" rel="noopener noreferrer"&gt;guia da OCI&lt;/a&gt; deste site explica como gerar a API key e colá-la no Control Cloud. O Search Console mostra gente chegando com buscas como "user ocid" e "oci auth", ou seja, o passo anterior: onde está cada dado e por que a CLI responde 401. Esta nota junta os caminhos do console para o OCID do usuário, o da tenancy e o de um compartment, a região e a fingerprint, como fica o ~/.oci/config, como testá-lo com a CLI e as causas de NotAuthenticated que a Oracle documenta. Tudo sai de docs.oracle.com; a lista de fontes está no final.&lt;/p&gt;

&lt;h2&gt;
  
  
  Onde está cada dado no console
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;User OCID (ocid1.user.oc1..): menu Profile, no canto superior direito → User settings. Fica em User Information, com um botão Copy. Se você é administrador e procura o de outro usuário: Identity &amp;amp; Security → Identity → Users, e o nome do usuário.&lt;/li&gt;
&lt;li&gt;Tenancy OCID (ocid1.tenancy.oc1..): menu Profile → Tenancy: . Fica em Tenancy Information; Show mostra o valor inteiro e Copy o copia.&lt;/li&gt;
&lt;li&gt;Compartment OCID: Identity &amp;amp; Security → Identity → Compartments. A lista mostra uma versão encurtada do OCID ao lado de cada compartment; um clique mostra o valor inteiro, com o seu Copy. O compartment raiz é a tenancy, então o OCID dele é o tenancy OCID.&lt;/li&gt;
&lt;li&gt;Fingerprint: o console a mostra quando você envia a chave pública para o usuário, no formato 12:34:56:78:90🆎cd:ef:.... Ela identifica qual das chaves do usuário assina a requisição: a Oracle permite até três API keys por usuário.&lt;/li&gt;
&lt;li&gt;Região: um identificador como us-ashburn-1. O snippet de configuração que o console monta usa a região que estava selecionada naquele momento, não necessariamente a home.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  O caminho curto: Add API Key e o snippet do console
&lt;/h2&gt;

&lt;p&gt;Em User settings, a seção de API keys do usuário (conforme a versão do console aparece como API Keys ou como Token and keys) → Add API Key → Download Private Key → Add. Ao adicioná-la, o console mostra um Configuration File Preview com os cinco campos que o arquivo pede. Copie-o como está para ~/.oci/config e só corrija key_file com o caminho onde você salvou a chave privada. Se você o perdeu, dá para recuperar: na lista de fingerprints, menu de ações (três pontos) → View configuration file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="k"&gt;[DEFAULT]&lt;/span&gt;
&lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ocid1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;oc1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;unique_ID&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="na"&gt;fingerprint&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;34&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;56&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;78&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;90&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;ab&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;cd&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;ef&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;34&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;56&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;78&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;90&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;ab&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;cd&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;ef&lt;/span&gt;
&lt;span class="na"&gt;key_file&lt;/span&gt;&lt;span class="o"&gt;=~/&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;oci&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;oci_api_key&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pem&lt;/span&gt;
&lt;span class="na"&gt;tenancy&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ocid1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tenancy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;oc1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;unique_ID&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="na"&gt;region&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;us&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;ashburn&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O local padrão é ~/.oci/config; no PowerShell é $Env:UserProfile.oci\config. A chave tem que estar em formato PEM, RSA de pelo menos 2048 bits, e não é a chave SSH das instâncias. A entrada pass_phrase existe para chaves cifradas, mas a Oracle a marca como obsoleta e recomenda passar a frase em tempo de execução.&lt;/p&gt;

&lt;h2&gt;
  
  
  Com a CLI: oci setup config
&lt;/h2&gt;

&lt;p&gt;Se preferir não usar o console para o arquivo, oci setup config pergunta os OCIDs e a região, gera o par de chaves e escreve o arquivo. O que ele não faz é enviar a chave pública: isso continua sendo Add API Key → Paste a public key no console, e é ali que aparece a fingerprint. Para comparar a fingerprint da sua chave local com a do console, a Oracle dá este comando do OpenSSL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# pergunta OCIDs e região, gera as chaves e escreve ~/.oci/config&lt;/span&gt;
oci setup config

&lt;span class="c"&gt;# fingerprint da chave privada local: tem que coincidir com a do console&lt;/span&gt;
openssl rsa -pubout -outform DER -in ~/.oci/oci_api_key.pem &lt;span class="o"&gt;|&lt;/span&gt; openssl md5 -c

&lt;span class="c"&gt;# só você deve conseguir ler a chave&lt;/span&gt;
chmod go-rwx ~/.oci/oci_api_key.pem
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Testar se autentica
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# regiões em que a tenancy está inscrita (e qual é a home)&lt;/span&gt;
oci iam region-subscription list

&lt;span class="c"&gt;# os dados do usuário dono da chave&lt;/span&gt;
oci iam user get --user-id ocid1.user.oc1..&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;unique_ID&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Se qualquer um dos dois responder com dados, a assinatura é válida: o user OCID, a fingerprint, a chave e a tenancy estão certos. Um NotAuthorizedOrNotFound (404) é outra coisa: autenticou, mas a policy do grupo do usuário não o deixa ver esse recurso.&lt;/p&gt;

&lt;h2&gt;
  
  
  NotAuthenticated (401): as causas que a Oracle lista
&lt;/h2&gt;

&lt;p&gt;A Oracle documenta o 401 NotAuthenticated como "The required authentication information was not provided or was incorrect", e enumera estas causas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Um dado que falta ou está errado: tenancy OCID, user OCID, fingerprint ou chave privada. O caso típico é o OCID de outro usuário, não daquele para quem a chave foi enviada.&lt;/li&gt;
&lt;li&gt;key_file aponta para a chave pública em vez da privada.&lt;/li&gt;
&lt;li&gt;Um par de chaves no formato errado: tem que ser PEM, não ssh-rsa.&lt;/li&gt;
&lt;li&gt;A chave pública dessa fingerprint não foi enviada para o usuário que assina (ou alguém a apagou).&lt;/li&gt;
&lt;li&gt;A tenancy não está inscrita na região configurada em region.&lt;/li&gt;
&lt;li&gt;O usuário não está em um grupo com as permissões que a operação pede.&lt;/li&gt;
&lt;li&gt;O relógio da máquina está defasado mais de cinco minutos em relação ao servidor.&lt;/li&gt;
&lt;li&gt;Em clientes próprios, falta um dado ou o header Authorization da assinatura está malformado.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;O relógio se descarta rápido comparando a sua hora em UTC com o header Date que qualquer endpoint da API devolve:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;date -u
&lt;span class="c"&gt;# o header Date do endpoint da API que você estiver usando&lt;/span&gt;
curl -s --head &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;endpoint&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; grep Date
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  O que o Control Cloud pede
&lt;/h2&gt;

&lt;p&gt;O formulário da OCI no Control Cloud pede quatro dados: tenancy OCID, user OCID, fingerprint e o conteúdo da chave privada, colado (não o caminho do arquivo). Não pede região nem compartment. Com essa chave ele lista as regiões inscritas da tenancy, começa lendo a home e as demais são ativadas pelo app; quais compartments e recursos ele vê depende da policy do grupo do usuário, que o &lt;a href="https://controlcloud.dev/pt/guias/oci" rel="noopener noreferrer"&gt;guia da OCI&lt;/a&gt; monta como somente leitura. As credenciais ficam cifradas. Se quiser testar com a sua tenancy, &lt;a href="https://controlcloud.dev/pt/contact" rel="noopener noreferrer"&gt;peça acesso&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fontes
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Oracle, "Required Keys and OCIDs" (gerar e enviar a API key, fingerprint, OCIDs da tenancy e do usuário).&lt;/li&gt;
&lt;li&gt;Oracle, "SDK and CLI Configuration File" (campos e local do ~/.oci/config).&lt;/li&gt;
&lt;li&gt;Oracle, "Installing the CLI" (oci setup config e verificação do arquivo) e a referência de oci iam region-subscription list e oci iam user get.&lt;/li&gt;
&lt;li&gt;Oracle, "Locating Oracle Cloud Infrastructure IDs" (OCID de um compartment).&lt;/li&gt;
&lt;li&gt;Oracle, "API Errors" (401 NotAuthenticated) e "REST APIs" (desvio máximo de relógio).&lt;/li&gt;
&lt;li&gt;Neste repositório: o formulário de conexão da OCI (ConnectionsPage.tsx) e a descoberta de regiões (orgcreds/livefetch.go).&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>braziliandevs</category>
      <category>oracle</category>
      <category>cloud</category>
      <category>devops</category>
    </item>
    <item>
      <title>OCID de usuario en OCI: dónde están el user OCID, el tenancy OCID, el compartment y la fingerprint, y por qué la CLI responde...</title>
      <dc:creator>Gonzalo Gjuka</dc:creator>
      <pubDate>Fri, 13 Nov 2026 12:27:34 +0000</pubDate>
      <link>https://dev.to/gonzalogjuka/ocid-de-usuario-en-oci-donde-estan-el-user-ocid-el-tenancy-ocid-el-compartment-y-la-fingerprint-4352</link>
      <guid>https://dev.to/gonzalogjuka/ocid-de-usuario-en-oci-donde-estan-el-user-ocid-el-tenancy-ocid-el-compartment-y-la-fingerprint-4352</guid>
      <description>&lt;p&gt;La &lt;a href="https://controlcloud.dev/guias/oci" rel="noopener noreferrer"&gt;guía de OCI&lt;/a&gt; de este sitio explica cómo generar la API key y pegarla en Control Cloud. Search Console muestra a gente que llega buscando "user ocid" y "oci auth", o sea, el paso anterior: dónde está cada dato y por qué la CLI contesta 401. Esta nota junta las rutas de la consola para el OCID de usuario, el de la tenancy y el de un compartment, la región y la fingerprint, cómo queda ~/.oci/config, cómo probarlo con la CLI y las causas de NotAuthenticated que documenta Oracle. Todo sale de docs.oracle.com; la lista de fuentes está al final.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dónde está cada dato en la consola
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;User OCID (ocid1.user.oc1..): menú Profile, arriba a la derecha → User settings. Está bajo User Information, con un botón Copy. Si sos administrador y buscás el de otro usuario: Identity &amp;amp; Security → Identity → Users, y el nombre del usuario.&lt;/li&gt;
&lt;li&gt;Tenancy OCID (ocid1.tenancy.oc1..): menú Profile → Tenancy: . Está bajo Tenancy Information; Show lo muestra entero y Copy lo copia.&lt;/li&gt;
&lt;li&gt;Compartment OCID: Identity &amp;amp; Security → Identity → Compartments. La lista muestra una versión acortada del OCID al lado de cada compartment; un clic lo muestra entero, con su Copy. El compartment raíz es la tenancy, así que su OCID es el tenancy OCID.&lt;/li&gt;
&lt;li&gt;Fingerprint: la muestra la consola cuando subís la clave pública al usuario, con la forma 12:34:56:78:90🆎cd:ef:.... Identifica cuál de las claves del usuario firma el pedido: Oracle permite hasta tres API keys por usuario.&lt;/li&gt;
&lt;li&gt;Región: un identificador como us-ashburn-1. El snippet de configuración que arma la consola usa la región que tenías seleccionada en ese momento, no necesariamente la home.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  El camino corto: Add API Key y el snippet de la consola
&lt;/h2&gt;

&lt;p&gt;En User settings, la sección de API keys del usuario (según la versión de la consola figura como API Keys o como Token and keys) → Add API Key → Download Private Key → Add. Al agregarla, la consola muestra un Configuration File Preview con los cinco campos que pide el archivo. Se copia tal cual a ~/.oci/config y sólo hay que corregir key_file con la ruta donde guardaste la clave privada. Si lo perdiste, se recupera: en la lista de fingerprints, menú de acciones (tres puntos) → View configuration file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="k"&gt;[DEFAULT]&lt;/span&gt;
&lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ocid1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;oc1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;unique_ID&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="na"&gt;fingerprint&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;34&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;56&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;78&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;90&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;ab&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;cd&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;ef&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;34&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;56&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;78&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;90&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;ab&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;cd&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;ef&lt;/span&gt;
&lt;span class="na"&gt;key_file&lt;/span&gt;&lt;span class="o"&gt;=~/&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;oci&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;oci_api_key&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pem&lt;/span&gt;
&lt;span class="na"&gt;tenancy&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ocid1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tenancy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;oc1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;unique_ID&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="na"&gt;region&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;us&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;ashburn&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;La ubicación por defecto es ~/.oci/config; en PowerShell es $Env:UserProfile.oci\config. La clave tiene que estar en formato PEM, RSA de al menos 2048 bits, y no es la clave SSH de las instancias. La entrada pass_phrase existe para claves cifradas, pero Oracle la marca como deprecada y recomienda pasar la frase en tiempo de ejecución.&lt;/p&gt;

&lt;h2&gt;
  
  
  Con la CLI: oci setup config
&lt;/h2&gt;

&lt;p&gt;Si preferís no pasar por la consola para el archivo, oci setup config pregunta los OCIDs y la región, genera el par de claves y escribe el archivo. Lo que no hace es subir la clave pública: eso sigue siendo Add API Key → Paste a public key en la consola, y ahí aparece la fingerprint. Para comparar la fingerprint de tu clave local con la de la consola, Oracle da este comando de OpenSSL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# pregunta OCIDs y región, genera las claves y escribe ~/.oci/config&lt;/span&gt;
oci setup config

&lt;span class="c"&gt;# fingerprint de la clave privada local: tiene que coincidir con la de la consola&lt;/span&gt;
openssl rsa -pubout -outform DER -in ~/.oci/oci_api_key.pem &lt;span class="o"&gt;|&lt;/span&gt; openssl md5 -c

&lt;span class="c"&gt;# que sólo vos puedas leer la clave&lt;/span&gt;
chmod go-rwx ~/.oci/oci_api_key.pem
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Probar que autentica
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# regiones a las que está suscripta la tenancy (y cuál es la home)&lt;/span&gt;
oci iam region-subscription list

&lt;span class="c"&gt;# los datos del usuario dueño de la clave&lt;/span&gt;
oci iam user get --user-id ocid1.user.oc1..&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;unique_ID&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Si cualquiera de las dos responde con datos, la firma es válida: el user OCID, la fingerprint, la clave y la tenancy están bien. Un NotAuthorizedOrNotFound (404) es otra cosa: autenticó, pero la policy del grupo del usuario no le deja ver ese recurso.&lt;/p&gt;

&lt;h2&gt;
  
  
  NotAuthenticated (401): las causas que lista Oracle
&lt;/h2&gt;

&lt;p&gt;Oracle documenta el 401 NotAuthenticated como "The required authentication information was not provided or was incorrect", y enumera estas causas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Un dato que falta o está mal: tenancy OCID, user OCID, fingerprint o clave privada. El caso típico es un user OCID de otro usuario, no del que tiene subida la clave.&lt;/li&gt;
&lt;li&gt;key_file apunta a la clave pública en vez de la privada.&lt;/li&gt;
&lt;li&gt;Un par de claves en formato incorrecto: tiene que ser PEM, no el formato ssh-rsa.&lt;/li&gt;
&lt;li&gt;La clave pública de esa fingerprint no está subida para el usuario que firma (o alguien la borró).&lt;/li&gt;
&lt;li&gt;La tenancy no está suscripta a la región configurada en region.&lt;/li&gt;
&lt;li&gt;El usuario no está en un grupo con los permisos que pide la operación.&lt;/li&gt;
&lt;li&gt;El reloj de la máquina está desfasado más de cinco minutos respecto del servidor.&lt;/li&gt;
&lt;li&gt;En clientes propios, falta un dato o está mal armado el header Authorization de la firma.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;El reloj se descarta rápido comparando tu hora en UTC con el header Date que devuelve cualquier endpoint de la API:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;date -u
&lt;span class="c"&gt;# el header Date del endpoint de la API que estés usando&lt;/span&gt;
curl -s --head &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;endpoint&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; grep Date
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Lo que pide Control Cloud
&lt;/h2&gt;

&lt;p&gt;El formulario de OCI en Control Cloud pide cuatro datos: tenancy OCID, user OCID, fingerprint y el contenido de la clave privada, pegado (no la ruta del archivo). No pide región ni compartment. Con esa clave lista las regiones suscriptas de la tenancy, empieza leyendo la home y las demás se activan desde la app; qué compartments y recursos ve depende de la policy del grupo del usuario, que la &lt;a href="https://controlcloud.dev/guias/oci" rel="noopener noreferrer"&gt;guía de OCI&lt;/a&gt; arma de solo lectura. Las credenciales quedan cifradas. Si querés probarlo con tu tenancy, &lt;a href="https://controlcloud.dev/contact" rel="noopener noreferrer"&gt;pedí acceso&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fuentes
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Oracle, "Required Keys and OCIDs" (generar y subir la API key, fingerprint, OCIDs de tenancy y usuario).&lt;/li&gt;
&lt;li&gt;Oracle, "SDK and CLI Configuration File" (campos y ubicación de ~/.oci/config).&lt;/li&gt;
&lt;li&gt;Oracle, "Installing the CLI" (oci setup config y verificación del archivo) y la referencia de oci iam region-subscription list y oci iam user get.&lt;/li&gt;
&lt;li&gt;Oracle, "Locating Oracle Cloud Infrastructure IDs" (OCID de un compartment).&lt;/li&gt;
&lt;li&gt;Oracle, "API Errors" (401 NotAuthenticated) y "REST APIs" (desfase de reloj máximo).&lt;/li&gt;
&lt;li&gt;En este repositorio: el formulario de conexión de OCI (ConnectionsPage.tsx) y el descubrimiento de regiones (orgcreds/livefetch.go).&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>oracle</category>
      <category>cloud</category>
      <category>devops</category>
      <category>cli</category>
    </item>
    <item>
      <title>Service principal Azure somente leitura: papel Reader, custos, vencimento do secret e como comprovar que não escreve</title>
      <dc:creator>Gonzalo Gjuka</dc:creator>
      <pubDate>Fri, 13 Nov 2026 12:27:03 +0000</pubDate>
      <link>https://dev.to/gonzalogjuka/service-principal-azure-somente-leitura-papel-reader-custos-vencimento-do-secret-e-como-3a3d</link>
      <guid>https://dev.to/gonzalogjuka/service-principal-azure-somente-leitura-papel-reader-custos-vencimento-do-secret-e-como-3a3d</guid>
      <description>&lt;p&gt;O &lt;a href="https://controlcloud.dev/pt/guias/azure" rel="noopener noreferrer"&gt;guia do Azure&lt;/a&gt; deste site explica em cinco passos como criar o service principal pelo portal e colá-lo no Control Cloud. O Search Console mostra buscas que chegam sem uma página que as responda: "azure service principal", "service principal azure", "controle de acesso no azure". Esta nota responde com a CLI: o que é um service principal e como ele difere de um app registration e de uma managed identity, como criá-lo com o papel Reader em um único comando, qual papel acrescenta a leitura de custos, quando o secret vence e como comprovar que ele de fato não pode escrever. Os comandos e os nomes dos papéis saem do Microsoft Learn; a lista de fontes está no final.&lt;/p&gt;

&lt;h2&gt;
  
  
  App registration, service principal e managed identity
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;App registration (o application object): a definição global da aplicação, que fica no tenant onde foi registrada. Ali estão o client ID e as credenciais.&lt;/li&gt;
&lt;li&gt;Service principal: segundo o Microsoft Learn, a representação local desse application object em um tenant. É a identidade à qual o Azure RBAC atribui papéis. Ao registrar uma aplicação, o service principal é criado automaticamente; por isso no portal os dois parecem a mesma coisa.&lt;/li&gt;
&lt;li&gt;Managed identity: um service principal que o Azure administra para código que roda dentro do Azure (uma VM, uma Function). Não há secret para guardar nem rotacionar. Se a ferramenta que lê sua conta roda fora do Azure, não serve: você precisa de um service principal com secret ou certificado.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Criá-lo com o papel Reader em um único comando
&lt;/h2&gt;

&lt;p&gt;az ad sp create-for-rbac registra a aplicação, cria o service principal e atribui o papel em um passo. O Microsoft Learn recomenda passar sempre --role e --scopes: sem eles o service principal existe, mas não tem acesso a nenhum recurso. Para criá-lo você precisa de User Access Administrator ou Role Based Access Control Administrator (ou superior) na assinatura.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# a assinatura ativa da sua sessão do az&lt;/span&gt;
&lt;span class="nv"&gt;SUB&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$(&lt;/span&gt;az account show --query id --output tsv&lt;span class="o"&gt;)&lt;/span&gt;

az ad sp create-for-rbac --name &lt;span class="s2"&gt;"inventario-somente-leitura"&lt;/span&gt; \
  --role Reader \
  --scopes &lt;span class="s2"&gt;"/subscriptions/$SUB"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A saída é um JSON com quatro campos:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;"appId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"00000000-0000-0000-0000-000000000000"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"displayName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"inventario-somente-leitura"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"password"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"tenant"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"00000000-0000-0000-0000-000000000000"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;appId é o client ID, password é o client secret e tenant é o tenant ID. O secret não pode ser lido de novo: se você perdê-lo, gere outro com az ad sp credential reset. Dois avisos da documentação: se outro objeto do diretório tiver o mesmo display name, o comando pode modificá-lo, então use um nome único; e se o seu diretório tiver "Users can register applications" em No, o comando falha com "Insufficient privileges to complete the operation" até que um admin do Entra lhe dê um papel como Application Developer.&lt;/p&gt;

&lt;p&gt;Reader é o papel built-in que a Microsoft descreve como "View all resources, but does not allow you to make any changes". Sua única ação é */read no control plane e ele não tem DataActions: vê que uma storage account existe, mas não lê os blobs guardados nela.&lt;/p&gt;

&lt;h2&gt;
  
  
  Menos alcance: resource groups em vez da assinatura
&lt;/h2&gt;

&lt;p&gt;--scopes aceita vários scopes separados por espaços. Se a ferramenta só precisa ver dois resource groups, dê acesso a eles e não à assinatura inteira:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;az ad sp create-for-rbac --name &lt;span class="s2"&gt;"inventario-dois-rg"&lt;/span&gt; \
  --role Reader \
  --scopes &lt;span class="s2"&gt;"/subscriptions/$SUB/resourceGroups/prod-web"&lt;/span&gt; \
           &lt;span class="s2"&gt;"/subscriptions/$SUB/resourceGroups/prod-db"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O preço de reduzir o scope é que o inventário também diminui: o que estiver fora desses resource groups não aparece. Para um inventário completo, o scope certo é a assinatura; para uma equipe que só opera a sua parte, o resource group.&lt;/p&gt;

&lt;h2&gt;
  
  
  Custos: Reader não basta
&lt;/h2&gt;

&lt;p&gt;Para ver custos no nível da assinatura ou do resource group, o Microsoft Learn exige pelo menos Cost Management Reader (ou Contributor). Em contratos Enterprise também é preciso ativar "AO view charges", e no Microsoft Customer Agreement a opção "Azure charges" do perfil de cobrança. No código do Control Cloud, a consulta à API do Cost Management com um service principal que só tem Reader volta com 403. O papel é acrescentado ao mesmo service principal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;az role assignment create --assignee &lt;span class="s2"&gt;"$APP_ID"&lt;/span&gt; \
  --role &lt;span class="s2"&gt;"Cost Management Reader"&lt;/span&gt; \
  --scope &lt;span class="s2"&gt;"/subscriptions/$SUB"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Atribuí-lo exige Owner ou User Access Administrator nesse scope. Cost Management Reader lê custos e não modifica recursos, então a credencial continua sem poder escrever.&lt;/p&gt;

&lt;h2&gt;
  
  
  O secret vence: quanto dura e como rotacioná-lo
&lt;/h2&gt;

&lt;p&gt;create-for-rbac cria o secret com um ano de validade se você não passar --years. Pelo portal, em Certificates &amp;amp; secrets, o máximo é 24 meses e a Microsoft recomenda menos de 12. A mesma página recomenda certificados ou credenciais federadas em vez de secrets em produção. Se você usa secret, a rotação sem interrupção é adicionar um novo antes de apagar o anterior:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# quando vence cada credencial (o valor não pode ser recuperado)&lt;/span&gt;
az ad app credential list --id &lt;span class="s2"&gt;"$APP_ID"&lt;/span&gt;

&lt;span class="c"&gt;# um novo, SEM apagar o atual&lt;/span&gt;
az ad app credential reset --id &lt;span class="s2"&gt;"$APP_ID"&lt;/span&gt; --append --years 1

&lt;span class="c"&gt;# quando o consumidor já usa o novo, o antigo é apagado pelo keyId&lt;/span&gt;
az ad app credential delete --id &lt;span class="s2"&gt;"$APP_ID"&lt;/span&gt; --key-id &lt;span class="s2"&gt;"$KEY_ID_ANTIGO"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sem --append, o reset substitui as credenciais existentes e quem usava a anterior deixa de autenticar na hora. Anote o vencimento: no dia em que vence, a integração para de ler sem outro aviso além de um erro de login.&lt;/p&gt;

&lt;h2&gt;
  
  
  Comprovar que ele só lê
&lt;/h2&gt;

&lt;p&gt;Antes de colá-lo em qualquer lugar, faça login com o service principal e teste as duas coisas. read -sp evita que o secret fique no histórico do shell:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;read&lt;/span&gt; -sp &lt;span class="s2"&gt;"client secret: "&lt;/span&gt; AZ_PASS &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt;
az login --service-principal -u &lt;span class="s2"&gt;"$APP_ID"&lt;/span&gt; -p &lt;span class="s2"&gt;"$AZ_PASS"&lt;/span&gt; --tenant &lt;span class="s2"&gt;"$TENANT"&lt;/span&gt;

&lt;span class="c"&gt;# ler: tem que funcionar&lt;/span&gt;
az group list --output table

&lt;span class="c"&gt;# escrever: tem que falhar com AuthorizationFailed&lt;/span&gt;
az group create --name teste-escrita --location eastus

&lt;span class="c"&gt;# quais papéis ele tem e em qual scope&lt;/span&gt;
az role assignment list --assignee &lt;span class="s2"&gt;"$APP_ID"&lt;/span&gt; --all --output table
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A criação do resource group tem que falhar com AuthorizationFailed e uma mensagem dizendo que o cliente "does not have authorization to perform action" nesse scope. Se ele for criado, o service principal tem um papel de escrita herdado de outro lugar: a última linha mostra qual e em qual scope. Apague o resource group de teste com o seu usuário e retire esse papel.&lt;/p&gt;

&lt;h2&gt;
  
  
  Usá-lo no Control Cloud
&lt;/h2&gt;

&lt;p&gt;O formulário do Azure no Control Cloud pede tenant ID, client ID e client secret; o subscription ID é opcional. Vazio, o Control Cloud descobre todas as assinaturas às quais esse service principal tem acesso, então o scope que você deu acima decide o que aparece no mapa; com um ID, lê só essa assinatura. As credenciais ficam cifradas. Com Reader ele desenha resource groups, VMs e redes; o painel de custos precisa também de Cost Management Reader e, quando falta, mostra estes mesmos passos. Ligar ou desligar VMs pelo Control Cloud exige um papel com permissão de escrita nas VMs, como explica o &lt;a href="https://controlcloud.dev/pt/guias/azure" rel="noopener noreferrer"&gt;guia do Azure&lt;/a&gt;. Se quiser testar com a sua assinatura, &lt;a href="https://controlcloud.dev/pt/contact" rel="noopener noreferrer"&gt;peça acesso&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fontes
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Microsoft Learn, "Create Azure service principals using the Azure CLI" e "Reset service principal credentials using the Azure CLI".&lt;/li&gt;
&lt;li&gt;Microsoft Learn, "Application and service principal objects in Microsoft Entra ID" e "What are managed identities for Azure resources?".&lt;/li&gt;
&lt;li&gt;Microsoft Learn, "Add and manage app credentials in Microsoft Entra ID" (vencimento dos client secrets).&lt;/li&gt;
&lt;li&gt;Microsoft Learn, "Azure built-in roles for General" (papel Reader) e "Assign access to Cost Management data".&lt;/li&gt;
&lt;li&gt;Referência da CLI: az ad sp, az ad app credential e az role assignment.&lt;/li&gt;
&lt;li&gt;Neste repositório: o formulário de conexão do Azure (ConnectionsPage.tsx), a descoberta de assinaturas (orgcreds/livefetch.go) e a consulta de custos (azuremap/cost.go).&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>braziliandevs</category>
      <category>azure</category>
      <category>devops</category>
      <category>security</category>
    </item>
    <item>
      <title>Service principal de Azure de solo lectura: Reader, costos, vencimiento del secret y cómo comprobar que no escribe</title>
      <dc:creator>Gonzalo Gjuka</dc:creator>
      <pubDate>Fri, 13 Nov 2026 12:26:27 +0000</pubDate>
      <link>https://dev.to/gonzalogjuka/service-principal-de-azure-de-solo-lectura-reader-costos-vencimiento-del-secret-y-como-comprobar-1bcb</link>
      <guid>https://dev.to/gonzalogjuka/service-principal-de-azure-de-solo-lectura-reader-costos-vencimiento-del-secret-y-como-comprobar-1bcb</guid>
      <description>&lt;p&gt;La &lt;a href="https://controlcloud.dev/guias/azure" rel="noopener noreferrer"&gt;guía de Azure&lt;/a&gt; de este sitio explica en cinco pasos cómo crear el service principal desde el portal y pegarlo en Control Cloud. Search Console muestra búsquedas que llegan sin una página que las responda: "azure service principal", "service principal azure", "control de acceso en azure". Esta nota las responde con la CLI: qué es un service principal y en qué se diferencia de un app registration y de una managed identity, cómo crearlo con el rol Reader en un solo comando, qué rol suma la lectura de costos, cuándo vence el secret y cómo comprobar que de verdad no puede escribir. Los comandos y los nombres de roles salen de Microsoft Learn; la lista de fuentes está al final.&lt;/p&gt;

&lt;h2&gt;
  
  
  App registration, service principal y managed identity
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;App registration (el application object): la definición global de la aplicación, que vive en el tenant donde se registró. Ahí están el client ID y las credenciales.&lt;/li&gt;
&lt;li&gt;Service principal: según Microsoft Learn, la representación local de ese application object en un tenant. Es la identidad a la que Azure RBAC le asigna roles. Al registrar una aplicación, el service principal se crea solo; por eso en el portal parecen la misma cosa.&lt;/li&gt;
&lt;li&gt;Managed identity: un service principal que administra Azure para código que corre dentro de Azure (una VM, una Function). No hay secreto que guardar ni rotar. Si la herramienta que lee tu cuenta corre afuera de Azure, no sirve: necesitás un service principal con secret o certificado.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Crearlo con el rol Reader en un solo comando
&lt;/h2&gt;

&lt;p&gt;az ad sp create-for-rbac registra la aplicación, crea el service principal y le asigna el rol en un paso. Microsoft Learn recomienda pasar siempre --role y --scopes: sin ellos el service principal existe, pero no tiene acceso a ningún recurso. Para crearlo necesitás User Access Administrator o Role Based Access Control Administrator (o más) en la suscripción.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# la suscripción activa de tu sesión de az&lt;/span&gt;
&lt;span class="nv"&gt;SUB&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$(&lt;/span&gt;az account show --query id --output tsv&lt;span class="o"&gt;)&lt;/span&gt;

az ad sp create-for-rbac --name &lt;span class="s2"&gt;"inventario-solo-lectura"&lt;/span&gt; \
  --role Reader \
  --scopes &lt;span class="s2"&gt;"/subscriptions/$SUB"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;La salida es un JSON con cuatro campos:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;"appId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"00000000-0000-0000-0000-000000000000"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"displayName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"inventario-solo-lectura"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"password"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"tenant"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"00000000-0000-0000-0000-000000000000"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;appId es el client ID, password es el client secret y tenant es el tenant ID. El secret no se puede volver a leer: si se pierde, se genera otro con az ad sp credential reset. Dos avisos de la documentación: si otro objeto del directorio tiene el mismo display name, el comando puede modificarlo, así que usá un nombre único; y si tu directorio tiene "Users can register applications" en No, el comando falla con "Insufficient privileges to complete the operation" hasta que un admin de Entra te dé un rol como Application Developer.&lt;/p&gt;

&lt;p&gt;Reader es el rol built-in que Microsoft describe como "View all resources, but does not allow you to make any changes". Su única acción es */read sobre el control plane y no tiene DataActions: ve que existe una storage account, pero no lee los blobs que guarda.&lt;/p&gt;

&lt;h2&gt;
  
  
  Menos alcance: resource groups en vez de la suscripción
&lt;/h2&gt;

&lt;p&gt;--scopes acepta varios scopes separados por espacios. Si la herramienta sólo tiene que ver dos resource groups, dáselos a ellos y no a la suscripción entera:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;az ad sp create-for-rbac --name &lt;span class="s2"&gt;"inventario-dos-rg"&lt;/span&gt; \
  --role Reader \
  --scopes &lt;span class="s2"&gt;"/subscriptions/$SUB/resourceGroups/prod-web"&lt;/span&gt; \
           &lt;span class="s2"&gt;"/subscriptions/$SUB/resourceGroups/prod-db"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;El costo de achicar el scope es que el inventario también se achica: lo que esté fuera de esos resource groups no aparece. Para un inventario completo, el scope correcto es la suscripción; para un equipo que sólo opera su parte, el resource group.&lt;/p&gt;

&lt;h2&gt;
  
  
  Costos: Reader no alcanza
&lt;/h2&gt;

&lt;p&gt;Para ver costos a nivel de suscripción o de resource group, Microsoft Learn pide al menos Cost Management Reader (o Contributor). Con acuerdos Enterprise también tiene que estar activado "AO view charges", y con Microsoft Customer Agreement la opción "Azure charges" del perfil de facturación. En el código de Control Cloud la consulta a la API de Cost Management con un service principal que sólo tiene Reader vuelve con 403. El rol se suma al mismo service principal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;az role assignment create --assignee &lt;span class="s2"&gt;"$APP_ID"&lt;/span&gt; \
  --role &lt;span class="s2"&gt;"Cost Management Reader"&lt;/span&gt; \
  --scope &lt;span class="s2"&gt;"/subscriptions/$SUB"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Asignarlo requiere Owner o User Access Administrator sobre ese scope. Cost Management Reader lee costos y no modifica recursos, así que la credencial sigue sin poder escribir.&lt;/p&gt;

&lt;h2&gt;
  
  
  El secret vence: cuánto dura y cómo rotarlo
&lt;/h2&gt;

&lt;p&gt;create-for-rbac crea el secret con un año de validez si no pasás --years. Desde el portal, en Certificates &amp;amp; secrets, el máximo es 24 meses y Microsoft recomienda menos de 12. La misma página recomienda certificados o credenciales federadas en lugar de secrets para producción. Si usás secret, la rotación sin corte es agregar uno nuevo antes de borrar el anterior:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# cuándo vence cada credencial (el valor no se puede recuperar)&lt;/span&gt;
az ad app credential list --id &lt;span class="s2"&gt;"$APP_ID"&lt;/span&gt;

&lt;span class="c"&gt;# uno nuevo, SIN borrar el actual&lt;/span&gt;
az ad app credential reset --id &lt;span class="s2"&gt;"$APP_ID"&lt;/span&gt; --append --years 1

&lt;span class="c"&gt;# cuando el consumidor ya usa el nuevo, el viejo se borra por su keyId&lt;/span&gt;
az ad app credential delete --id &lt;span class="s2"&gt;"$APP_ID"&lt;/span&gt; --key-id &lt;span class="s2"&gt;"$KEY_ID_VIEJO"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sin --append, reset reemplaza las credenciales existentes y el consumidor que usaba la anterior deja de autenticar en ese momento. Anotá el vencimiento: el día que vence, la integración deja de leer sin más aviso que un error de login.&lt;/p&gt;

&lt;h2&gt;
  
  
  Comprobar que sólo lee
&lt;/h2&gt;

&lt;p&gt;Antes de pegarlo en ningún lado, iniciá sesión con el service principal y probá las dos cosas. read -sp evita que el secret quede en el historial de la shell:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;read&lt;/span&gt; -sp &lt;span class="s2"&gt;"client secret: "&lt;/span&gt; AZ_PASS &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt;
az login --service-principal -u &lt;span class="s2"&gt;"$APP_ID"&lt;/span&gt; -p &lt;span class="s2"&gt;"$AZ_PASS"&lt;/span&gt; --tenant &lt;span class="s2"&gt;"$TENANT"&lt;/span&gt;

&lt;span class="c"&gt;# leer: tiene que andar&lt;/span&gt;
az group list --output table

&lt;span class="c"&gt;# escribir: tiene que fallar con AuthorizationFailed&lt;/span&gt;
az group create --name prueba-escritura --location eastus

&lt;span class="c"&gt;# qué roles tiene y sobre qué scope&lt;/span&gt;
az role assignment list --assignee &lt;span class="s2"&gt;"$APP_ID"&lt;/span&gt; --all --output table
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;La creación del resource group tiene que fallar con AuthorizationFailed y un mensaje que dice que el cliente "does not have authorization to perform action" sobre ese scope. Si se crea, el service principal tiene un rol de escritura heredado de otro lado: la última línea muestra cuál y sobre qué scope. Borrá el resource group de prueba con tu usuario y sacale ese rol.&lt;/p&gt;

&lt;h2&gt;
  
  
  Usarlo en Control Cloud
&lt;/h2&gt;

&lt;p&gt;El formulario de Azure en Control Cloud pide tenant ID, client ID y client secret; el subscription ID es opcional. Vacío, Control Cloud descubre todas las suscripciones a las que ese service principal tiene acceso, así que el scope que le diste arriba decide qué aparece en el mapa; con un ID, lee sólo esa suscripción. Las credenciales quedan cifradas. Con Reader se dibujan resource groups, VMs y redes; el panel de costos necesita además Cost Management Reader y, si falta, muestra estos mismos pasos. Prender o apagar VMs desde Control Cloud requiere un rol con permisos de escritura sobre las VMs, como indica la &lt;a href="https://controlcloud.dev/guias/azure" rel="noopener noreferrer"&gt;guía de Azure&lt;/a&gt;. Si querés probarlo con tu suscripción, &lt;a href="https://controlcloud.dev/contact" rel="noopener noreferrer"&gt;pedí acceso&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fuentes
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Microsoft Learn, "Create Azure service principals using the Azure CLI" y "Reset service principal credentials using the Azure CLI".&lt;/li&gt;
&lt;li&gt;Microsoft Learn, "Application and service principal objects in Microsoft Entra ID" y "What are managed identities for Azure resources?".&lt;/li&gt;
&lt;li&gt;Microsoft Learn, "Add and manage app credentials in Microsoft Entra ID" (vencimiento de los client secrets).&lt;/li&gt;
&lt;li&gt;Microsoft Learn, "Azure built-in roles for General" (rol Reader) y "Assign access to Cost Management data".&lt;/li&gt;
&lt;li&gt;Referencia de la CLI: az ad sp, az ad app credential y az role assignment.&lt;/li&gt;
&lt;li&gt;En este repositorio: el formulario de conexión de Azure (ConnectionsPage.tsx), el descubrimiento de suscripciones (orgcreds/livefetch.go) y la consulta de costos (azuremap/cost.go).&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>azure</category>
      <category>devops</category>
      <category>security</category>
      <category>cloud</category>
    </item>
    <item>
      <title>OCI user OCID: where to find the user OCID, tenancy OCID, compartment and fingerprint, and why the CLI returns 401</title>
      <dc:creator>Gonzalo Gjuka</dc:creator>
      <pubDate>Fri, 13 Nov 2026 12:25:32 +0000</pubDate>
      <link>https://dev.to/gonzalogjuka/oci-user-ocid-where-to-find-the-user-ocid-tenancy-ocid-compartment-and-fingerprint-and-why-the-33j2</link>
      <guid>https://dev.to/gonzalogjuka/oci-user-ocid-where-to-find-the-user-ocid-tenancy-ocid-compartment-and-fingerprint-and-why-the-33j2</guid>
      <description>&lt;p&gt;This site's &lt;a href="https://controlcloud.dev/en/guias/oci" rel="noopener noreferrer"&gt;OCI guide&lt;/a&gt; explains how to generate the API key and paste it into Control Cloud. Search Console shows people arriving with searches like "user ocid" and "oci auth", which is the step before: where each value lives and why the CLI answers 401. This note gathers the Console paths for the user OCID, the tenancy OCID and a compartment OCID, the region and the fingerprint, what ~/.oci/config looks like, how to test it with the CLI, and the NotAuthenticated causes Oracle documents. Everything comes from docs.oracle.com; the source list is at the end.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where each value lives in the Console
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;User OCID (ocid1.user.oc1..): Profile menu, top right → User settings. It is under User Information, with a Copy button. If you are an administrator looking for another user's: Identity &amp;amp; Security → Identity → Users, then the user's name.&lt;/li&gt;
&lt;li&gt;Tenancy OCID (ocid1.tenancy.oc1..): Profile menu → Tenancy: . It is under Tenancy Information; Show displays it in full and Copy copies it.&lt;/li&gt;
&lt;li&gt;Compartment OCID: Identity &amp;amp; Security → Identity → Compartments. The list shows a shortened OCID next to each compartment; one click shows the full value, with its Copy. The root compartment is the tenancy, so its OCID is the tenancy OCID.&lt;/li&gt;
&lt;li&gt;Fingerprint: the Console shows it when you upload the public key to the user, in the form 12:34:56:78:90🆎cd:ef:.... It identifies which of the user's keys signs the request: Oracle allows up to three API keys per user.&lt;/li&gt;
&lt;li&gt;Region: an identifier such as us-ashburn-1. The configuration snippet the Console builds uses the region you had selected at that moment, not necessarily the home region.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The short path: Add API Key and the Console snippet
&lt;/h2&gt;

&lt;p&gt;In User settings, the user's API keys section (depending on the Console version it shows as API Keys or as Token and keys) → Add API Key → Download Private Key → Add. Once added, the Console shows a Configuration File Preview with the five fields the file needs. Copy it as is into ~/.oci/config and only fix key_file with the path where you saved the private key. If you lost it, you can get it back: in the fingerprint list, the Actions menu (three dots) → View configuration file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="k"&gt;[DEFAULT]&lt;/span&gt;
&lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ocid1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;oc1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;unique_ID&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="na"&gt;fingerprint&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;34&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;56&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;78&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;90&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;ab&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;cd&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;ef&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;34&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;56&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;78&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;90&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;ab&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;cd&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;ef&lt;/span&gt;
&lt;span class="na"&gt;key_file&lt;/span&gt;&lt;span class="o"&gt;=~/&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;oci&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;oci_api_key&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pem&lt;/span&gt;
&lt;span class="na"&gt;tenancy&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ocid1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tenancy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;oc1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;unique_ID&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="na"&gt;region&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;us&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;ashburn&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The default location is ~/.oci/config; in PowerShell it is $Env:UserProfile.oci\config. The key must be PEM, RSA with at least 2048 bits, and it is not the SSH key for your instances. The pass_phrase entry exists for encrypted keys, but Oracle marks it as deprecated and recommends passing the passphrase at runtime.&lt;/p&gt;

&lt;h2&gt;
  
  
  With the CLI: oci setup config
&lt;/h2&gt;

&lt;p&gt;If you would rather not use the Console for the file, oci setup config asks for the OCIDs and the region, generates the key pair and writes the file. What it does not do is upload the public key: that is still Add API Key → Paste a public key in the Console, and that is where the fingerprint appears. To compare your local key's fingerprint with the Console's, Oracle gives this OpenSSL command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# asks for OCIDs and region, generates the keys and writes ~/.oci/config&lt;/span&gt;
oci setup config

&lt;span class="c"&gt;# fingerprint of the local private key: it must match the Console's&lt;/span&gt;
openssl rsa -pubout -outform DER -in ~/.oci/oci_api_key.pem &lt;span class="o"&gt;|&lt;/span&gt; openssl md5 -c

&lt;span class="c"&gt;# only you should be able to read the key&lt;/span&gt;
chmod go-rwx ~/.oci/oci_api_key.pem
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Test that it authenticates
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# regions the tenancy is subscribed to (and which one is home)&lt;/span&gt;
oci iam region-subscription list

&lt;span class="c"&gt;# details of the user who owns the key&lt;/span&gt;
oci iam user get --user-id ocid1.user.oc1..&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;unique_ID&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If either one returns data, the signature is valid: the user OCID, fingerprint, key and tenancy are right. A NotAuthorizedOrNotFound (404) is something else: it authenticated, but the policy of the user's group does not let it see that resource.&lt;/p&gt;

&lt;h2&gt;
  
  
  NotAuthenticated (401): the causes Oracle lists
&lt;/h2&gt;

&lt;p&gt;Oracle documents 401 NotAuthenticated as "The required authentication information was not provided or was incorrect", and lists these causes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A value that is missing or wrong: tenancy OCID, user OCID, fingerprint or private key. The typical case is the OCID of a different user, not the one the key was uploaded for.&lt;/li&gt;
&lt;li&gt;key_file points to the public key instead of the private one.&lt;/li&gt;
&lt;li&gt;A key pair in the wrong format: it must be PEM, not ssh-rsa.&lt;/li&gt;
&lt;li&gt;The public key for that fingerprint is not uploaded for the signing user (or someone deleted it).&lt;/li&gt;
&lt;li&gt;The tenancy is not subscribed to the region set in region.&lt;/li&gt;
&lt;li&gt;The user is not in a group with the permissions the operation needs.&lt;/li&gt;
&lt;li&gt;The machine's clock is more than five minutes off from the server's.&lt;/li&gt;
&lt;li&gt;In custom clients, a piece of the signature's Authorization header is missing or malformed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The clock is quick to rule out by comparing your UTC time with the Date header any API endpoint returns:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;date -u
&lt;span class="c"&gt;# the Date header of the API endpoint you are using&lt;/span&gt;
curl -s --head &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;endpoint&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; grep Date
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What Control Cloud asks for
&lt;/h2&gt;

&lt;p&gt;Control Cloud's OCI form asks for four values: tenancy OCID, user OCID, fingerprint and the private key content, pasted (not the file path). It does not ask for a region or a compartment. With that key it lists the tenancy's subscribed regions, starts by reading the home region, and the others are enabled from the app; which compartments and resources it sees depends on the policy of the user's group, which the &lt;a href="https://controlcloud.dev/en/guias/oci" rel="noopener noreferrer"&gt;OCI guide&lt;/a&gt; sets up as read-only. Credentials are stored encrypted. If you want to try it with your tenancy, &lt;a href="https://controlcloud.dev/en/contact" rel="noopener noreferrer"&gt;request access&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Oracle, "Required Keys and OCIDs" (generating and uploading the API key, fingerprint, tenancy and user OCIDs).&lt;/li&gt;
&lt;li&gt;Oracle, "SDK and CLI Configuration File" (fields and location of ~/.oci/config).&lt;/li&gt;
&lt;li&gt;Oracle, "Installing the CLI" (oci setup config and verifying the file) and the reference for oci iam region-subscription list and oci iam user get.&lt;/li&gt;
&lt;li&gt;Oracle, "Locating Oracle Cloud Infrastructure IDs" (compartment OCID).&lt;/li&gt;
&lt;li&gt;Oracle, "API Errors" (401 NotAuthenticated) and "REST APIs" (maximum clock skew).&lt;/li&gt;
&lt;li&gt;In this repository: the OCI connection form (ConnectionsPage.tsx) and region discovery (orgcreds/livefetch.go).&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>oracle</category>
      <category>cloud</category>
      <category>devops</category>
      <category>cli</category>
    </item>
    <item>
      <title>Read-only Azure service principal: Reader role, costs, secret expiry and how to check it cannot write</title>
      <dc:creator>Gonzalo Gjuka</dc:creator>
      <pubDate>Fri, 13 Nov 2026 12:25:03 +0000</pubDate>
      <link>https://dev.to/gonzalogjuka/read-only-azure-service-principal-reader-role-costs-secret-expiry-and-how-to-check-it-cannot-548l</link>
      <guid>https://dev.to/gonzalogjuka/read-only-azure-service-principal-reader-role-costs-secret-expiry-and-how-to-check-it-cannot-548l</guid>
      <description>&lt;p&gt;This site's &lt;a href="https://controlcloud.dev/en/guias/azure" rel="noopener noreferrer"&gt;Azure guide&lt;/a&gt; explains in five steps how to create the service principal in the portal and paste it into Control Cloud. Search Console shows searches arriving with no page that answers them: "azure service principal", "service principal azure", "azure access control". This note answers them with the CLI: what a service principal is and how it differs from an app registration and a managed identity, how to create one with the Reader role in a single command, which role adds cost reading, when the secret expires, and how to check that it really cannot write. Commands and role names come from Microsoft Learn; the source list is at the end.&lt;/p&gt;

&lt;h2&gt;
  
  
  App registration, service principal and managed identity
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;App registration (the application object): the global definition of the application, living in the tenant where it was registered. That is where the client ID and the credentials are.&lt;/li&gt;
&lt;li&gt;Service principal: according to Microsoft Learn, the local representation of that application object in a tenant. It is the identity Azure RBAC assigns roles to. When you register an application, the service principal is created automatically, which is why the portal makes them look like one thing.&lt;/li&gt;
&lt;li&gt;Managed identity: a service principal that Azure manages for code running inside Azure (a VM, a Function). There is no secret to store or rotate. If the tool that reads your account runs outside Azure, it does not apply: you need a service principal with a secret or a certificate.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Create it with the Reader role in one command
&lt;/h2&gt;

&lt;p&gt;az ad sp create-for-rbac registers the application, creates the service principal and assigns the role in one step. Microsoft Learn recommends always passing --role and --scopes: without them the service principal exists but has no access to any resource. To create it you need User Access Administrator or Role Based Access Control Administrator (or higher) on the subscription.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# the active subscription of your az session&lt;/span&gt;
&lt;span class="nv"&gt;SUB&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$(&lt;/span&gt;az account show --query id --output tsv&lt;span class="o"&gt;)&lt;/span&gt;

az ad sp create-for-rbac --name &lt;span class="s2"&gt;"inventory-read-only"&lt;/span&gt; \
  --role Reader \
  --scopes &lt;span class="s2"&gt;"/subscriptions/$SUB"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output is a JSON document with four fields:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;"appId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"00000000-0000-0000-0000-000000000000"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"displayName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"inventory-read-only"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"password"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"tenant"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"00000000-0000-0000-0000-000000000000"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;appId is the client ID, password is the client secret and tenant is the tenant ID. The secret cannot be read again: if you lose it, generate another with az ad sp credential reset. Two warnings from the documentation: if another directory object shares the same display name, the command can modify it, so use a unique name; and if your directory has "Users can register applications" set to No, the command fails with "Insufficient privileges to complete the operation" until an Entra admin gives you a role such as Application Developer.&lt;/p&gt;

&lt;p&gt;Reader is the built-in role Microsoft describes as "View all resources, but does not allow you to make any changes". Its only action is */read on the control plane and it has no DataActions: it sees that a storage account exists, but does not read the blobs inside it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Less reach: resource groups instead of the subscription
&lt;/h2&gt;

&lt;p&gt;--scopes takes several space-separated scopes. If the tool only needs to see two resource groups, grant it those and not the whole subscription:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;az ad sp create-for-rbac --name &lt;span class="s2"&gt;"inventory-two-rgs"&lt;/span&gt; \
  --role Reader \
  --scopes &lt;span class="s2"&gt;"/subscriptions/$SUB/resourceGroups/prod-web"&lt;/span&gt; \
           &lt;span class="s2"&gt;"/subscriptions/$SUB/resourceGroups/prod-db"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The price of a smaller scope is a smaller inventory: anything outside those resource groups does not show up. For a full inventory the right scope is the subscription; for a team that only runs its own part, the resource group.&lt;/p&gt;

&lt;h2&gt;
  
  
  Costs: Reader is not enough
&lt;/h2&gt;

&lt;p&gt;To see costs at subscription or resource group scope, Microsoft Learn requires at least Cost Management Reader (or Contributor). Enterprise Agreements also need "AO view charges" turned on, and Microsoft Customer Agreements need the billing profile's "Azure charges" setting. In Control Cloud's code, the Cost Management API query from a service principal that only has Reader comes back as 403. The role is added to the same service principal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;az role assignment create --assignee &lt;span class="s2"&gt;"$APP_ID"&lt;/span&gt; \
  --role &lt;span class="s2"&gt;"Cost Management Reader"&lt;/span&gt; \
  --scope &lt;span class="s2"&gt;"/subscriptions/$SUB"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Assigning it requires Owner or User Access Administrator on that scope. Cost Management Reader reads costs and does not modify resources, so the credential still cannot write.&lt;/p&gt;

&lt;h2&gt;
  
  
  The secret expires: how long it lasts and how to rotate it
&lt;/h2&gt;

&lt;p&gt;create-for-rbac creates the secret with one year of validity unless you pass --years. In the portal, under Certificates &amp;amp; secrets, the maximum is 24 months and Microsoft recommends less than 12. The same page recommends certificates or federated credentials instead of secrets for production. If you use a secret, rotating without downtime means adding a new one before deleting the old one:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# when each credential expires (the value cannot be retrieved)&lt;/span&gt;
az ad app credential list --id &lt;span class="s2"&gt;"$APP_ID"&lt;/span&gt;

&lt;span class="c"&gt;# a new one, WITHOUT deleting the current one&lt;/span&gt;
az ad app credential reset --id &lt;span class="s2"&gt;"$APP_ID"&lt;/span&gt; --append --years 1

&lt;span class="c"&gt;# once the consumer uses the new one, delete the old one by its keyId&lt;/span&gt;
az ad app credential delete --id &lt;span class="s2"&gt;"$APP_ID"&lt;/span&gt; --key-id &lt;span class="s2"&gt;"$OLD_KEY_ID"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Without --append, reset replaces the existing credentials and whatever used the previous one stops authenticating at that moment. Write down the expiry date: the day it expires, the integration stops reading with no warning other than a sign-in error.&lt;/p&gt;

&lt;h2&gt;
  
  
  Check that it only reads
&lt;/h2&gt;

&lt;p&gt;Before pasting it anywhere, sign in as the service principal and test both things. read -sp keeps the secret out of the shell history:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;read&lt;/span&gt; -sp &lt;span class="s2"&gt;"client secret: "&lt;/span&gt; AZ_PASS &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt;
az login --service-principal -u &lt;span class="s2"&gt;"$APP_ID"&lt;/span&gt; -p &lt;span class="s2"&gt;"$AZ_PASS"&lt;/span&gt; --tenant &lt;span class="s2"&gt;"$TENANT"&lt;/span&gt;

&lt;span class="c"&gt;# read: must work&lt;/span&gt;
az group list --output table

&lt;span class="c"&gt;# write: must fail with AuthorizationFailed&lt;/span&gt;
az group create --name write-test --location eastus

&lt;span class="c"&gt;# which roles it has, and on which scope&lt;/span&gt;
az role assignment list --assignee &lt;span class="s2"&gt;"$APP_ID"&lt;/span&gt; --all --output table
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Creating the resource group must fail with AuthorizationFailed and a message saying the client "does not have authorization to perform action" over that scope. If it gets created, the service principal has a write role inherited from somewhere else: the last line shows which one and on which scope. Delete the test resource group with your own user and remove that role.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using it in Control Cloud
&lt;/h2&gt;

&lt;p&gt;Control Cloud's Azure form asks for the tenant ID, client ID and client secret; the subscription ID is optional. Left empty, Control Cloud discovers every subscription that service principal can access, so the scope you granted above decides what shows up on the map; with an ID, it reads only that subscription. Credentials are stored encrypted. With Reader it draws resource groups, VMs and networks; the cost panel also needs Cost Management Reader and, when it is missing, shows these same steps. Starting or stopping VMs from Control Cloud requires a role with write permissions on the VMs, as the &lt;a href="https://controlcloud.dev/en/guias/azure" rel="noopener noreferrer"&gt;Azure guide&lt;/a&gt; explains. If you want to try it with your subscription, &lt;a href="https://controlcloud.dev/en/contact" rel="noopener noreferrer"&gt;request access&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Microsoft Learn, "Create Azure service principals using the Azure CLI" and "Reset service principal credentials using the Azure CLI".&lt;/li&gt;
&lt;li&gt;Microsoft Learn, "Application and service principal objects in Microsoft Entra ID" and "What are managed identities for Azure resources?".&lt;/li&gt;
&lt;li&gt;Microsoft Learn, "Add and manage app credentials in Microsoft Entra ID" (client secret expiry).&lt;/li&gt;
&lt;li&gt;Microsoft Learn, "Azure built-in roles for General" (Reader role) and "Assign access to Cost Management data".&lt;/li&gt;
&lt;li&gt;CLI reference: az ad sp, az ad app credential and az role assignment.&lt;/li&gt;
&lt;li&gt;In this repository: the Azure connection form (ConnectionsPage.tsx), subscription discovery (orgcreds/livefetch.go) and the cost query (azuremap/cost.go).&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>azure</category>
      <category>devops</category>
      <category>security</category>
      <category>cloud</category>
    </item>
    <item>
      <title>Token de API de IONOS Cloud: el DCD no es el Control Center, cuánto dura y cómo hacerlo de solo lectura</title>
      <dc:creator>Gonzalo Gjuka</dc:creator>
      <pubDate>Thu, 12 Nov 2026 15:44:39 +0000</pubDate>
      <link>https://dev.to/gonzalogjuka/token-de-api-de-ionos-cloud-el-dcd-no-es-el-control-center-cuanto-dura-y-como-hacerlo-de-solo-18nd</link>
      <guid>https://dev.to/gonzalogjuka/token-de-api-de-ionos-cloud-el-dcd-no-es-el-control-center-cuanto-dura-y-como-hacerlo-de-solo-18nd</guid>
      <description>&lt;p&gt;La &lt;a href="https://controlcloud.dev/guias/ionos" rel="noopener noreferrer"&gt;guía de IONOS Cloud&lt;/a&gt; de este sitio explica en cuatro pasos cómo generar el token en el DCD y pegarlo. Search Console me mostró que la gente llega con preguntas que la guía no contesta: "ionos controlcenter", "dcd ionos", "ionos api token". Las junté acá: dónde se crea cada credencial de IONOS, cuánto dura el token, cómo se arma un usuario que de verdad sólo puede leer y cómo se revoca. Todo sale de la documentación de IONOS: docs.ionos.com, la referencia de la Auth API y la de ionosctl.&lt;/p&gt;

&lt;h2&gt;
  
  
  DCD, Control Center y Developer Portal: tres lugares, dos credenciales
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;El DCD (Data Center Designer, dcd.ionos.com) es la consola de IONOS Cloud: data centers virtuales, servidores, LANs, IP blocks. El token de la Cloud API se genera ahí, en Menu → Management → Token Manager.&lt;/li&gt;
&lt;li&gt;El panel de cliente de my.ionos.com, el que muchos buscan como "IONOS Control Center", es donde se contratan los productos. El token de la Cloud API no se crea en ese panel: el Token Manager vive en el DCD.&lt;/li&gt;
&lt;li&gt;El Developer Portal de hosting (developer.hosting.ionos.com) emite otra cosa: API keys con un public prefix y un secret, que se mandan en el header X-API-Key, después de activar el acceso a la API de hosting desde my.ionos.com. Según su propia documentación, esas APIs configuran productos de hosting (dominios, DNS, SSL). Si buscabas un token para los servidores del DCD, esa key no es.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;La Cloud API (&lt;a href="https://api.ionos.com/cloudapi/v6" rel="noopener noreferrer"&gt;https://api.ionos.com/cloudapi/v6&lt;/a&gt;) acepta el token como Authorization: Bearer o usuario y contraseña por Basic. El token es la opción que vence sola y se revoca sin cambiar la contraseña de nadie.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tres formas de generar el token, y cuánto dura
&lt;/h2&gt;

&lt;p&gt;Desde el DCD: Menu → Management → Token Manager → Generate Token. El TTL se elige de una lista (1 hora, 4 horas, 1, 7, 30, 60, 90, 180 o 365 días), y el valor del token se muestra una sola vez; en ese momento se puede descargar como archivo de texto. En la lista quedan el ID, la fecha de creación y la de vencimiento, nunca el valor.&lt;/p&gt;

&lt;p&gt;Por API o con ionosctl, el TTL va en segundos (o como 30D en la CLI), con un mínimo de 60 segundos y un máximo de un año, que también es el valor por defecto si no lo pedís:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Auth API: un token de 30 días (2.592.000 s) con usuario y contraseña&lt;/span&gt;
curl -s -u &lt;span class="s2"&gt;"$IONOS_USERNAME:$IONOS_PASSWORD"&lt;/span&gt; \
  &lt;span class="s2"&gt;"https://api.ionos.com/auth/v1/tokens/generate?ttl=2592000"&lt;/span&gt;

&lt;span class="c"&gt;# lo mismo con ionosctl&lt;/span&gt;
ionosctl token generate --ttl 30D

&lt;span class="c"&gt;# qué tokens tiene el usuario y cuándo vence cada uno&lt;/span&gt;
ionosctl token list
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Dos límites que figuran en la referencia de la Auth API. La autenticación Basic de ese endpoint sólo funciona si el usuario no tiene 2FA; con 2FA, el token se genera en el Token Manager del DCD. Y hay un tope de 100 tokens activos por usuario, que un script que genera uno nuevo en cada corrida alcanza rápido. ionosctl token delete --expired limpia los vencidos.&lt;/p&gt;

&lt;p&gt;Para una integración que sólo lee, yo elijo 90 días y anoto la fecha de vencimiento: ese día la integración deja de leer, y prefiero que pase con aviso. Un año es el valor por defecto de la API, no una recomendación.&lt;/p&gt;

&lt;h2&gt;
  
  
  Solo lectura: el token no tiene scopes, el usuario sí tiene límites
&lt;/h2&gt;

&lt;p&gt;El token no lleva permisos propios: actúa con la identidad del usuario que lo generó. El dueño del contrato y los administradores ven y pueden modificar todo (la página Users &amp;amp; Groups aclara que los administradores tienen acceso a todos los recursos del contrato sin estar en ningún grupo), así que un token generado desde tu usuario administrador es un token de administrador, lo uses para lo que lo uses. Un token de lectura sale de un usuario que no es administrador: mínimo privilegio armado con el RBAC de IONOS, que la misma página describe como usuarios, grupos, privilegios y recursos compartidos. En el DCD, Menu → Management → Users &amp;amp; Groups:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Users → Create: un usuario nuevo, con su email y contraseña, sin promoverlo a administrador.&lt;/li&gt;
&lt;li&gt;Groups → Create: un grupo, y en la pestaña Privileges no marcás nada. Los privilegios son acciones sobre el contrato (crear data centers, reservar IP blocks, crear snapshots o clusters de Kubernetes); un grupo de lectura no necesita ninguno.&lt;/li&gt;
&lt;li&gt;Members: sumás el usuario al grupo.&lt;/li&gt;
&lt;li&gt;Resources of Group → Grant Access: cada data center y cada IP block que tiene que ver. Según la documentación, asignar el recurso habilita la lectura; Edit y Share son casillas aparte, que quedan sin marcar.&lt;/li&gt;
&lt;li&gt;Entrás al DCD con ese usuario y generás el token en su Token Manager. Ese es el token que va en cualquier herramienta de inventario.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Con ionosctl se arma igual. La referencia de share create dice que el share, por defecto, da acceso de lectura y uso; editar es el flag --edit-privilege:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# grupo sin privilegios, con el usuario adentro&lt;/span&gt;
ionosctl compute group create --name &lt;span class="s2"&gt;"solo-lectura"&lt;/span&gt;
ionosctl compute group user add --group-id &lt;span class="s2"&gt;"$GROUP_ID"&lt;/span&gt; --user-id &lt;span class="s2"&gt;"$USER_ID"&lt;/span&gt;

&lt;span class="c"&gt;# IDs de los recursos que se pueden compartir&lt;/span&gt;
ionosctl compute resource list

&lt;span class="c"&gt;# un share por data center, sin --edit-privilege ni --share-privilege&lt;/span&gt;
ionosctl compute share create --group-id &lt;span class="s2"&gt;"$GROUP_ID"&lt;/span&gt; --resource-id &lt;span class="s2"&gt;"$DATACENTER_ID"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Antes de pegarlo en ningún lado, comprobá qué es. ionosctl token parse muestra el ID del token, el usuario, el contrato y el rol, y con --privileges los privilegios de ese usuario. La Cloud API dice qué data centers ve:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ionosctl token parse --token &lt;span class="s2"&gt;"$IONOS_TOKEN"&lt;/span&gt;
ionosctl token parse --token &lt;span class="s2"&gt;"$IONOS_TOKEN"&lt;/span&gt; --privileges

&lt;span class="c"&gt;# data centers visibles para este token&lt;/span&gt;
curl -s -H &lt;span class="s2"&gt;"Authorization: Bearer $IONOS_TOKEN"&lt;/span&gt; \
  &lt;span class="s2"&gt;"https://api.ionos.com/cloudapi/v6/datacenters?depth=1"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; jq -r &lt;span class="s1"&gt;'.items[].properties.name'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Si la lista sale vacía, el token está bien: la documentación avisa que los recursos que creó el dueño del contrato no son visibles para otros usuarios hasta que se comparten con su grupo. Falta el Grant Access de ese data center.&lt;/p&gt;

&lt;p&gt;Las métricas son un caso aparte. La referencia de ionosctl describe el privilegio "access and manage monitoring" como acceso a métricas y gestión de alarmas. Si querés ver CPU y red de los servidores con ese usuario, es el único privilegio que le sumaría, sabiendo que con él deja de ser lectura pura.&lt;/p&gt;

&lt;h2&gt;
  
  
  Revocar: Token Manager, API y el detalle de 2FA
&lt;/h2&gt;

&lt;p&gt;En el Token Manager, Delete invalida el token en el momento, aunque no haya vencido. Por API y CLI se borra por ID (DELETE /auth/v1/tokens/{tokenId}) o por criterio: CURRENT (el que estás usando), EXPIRED o ALL.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ionosctl token list &lt;span class="c"&gt;# ID, creación y vencimiento&lt;/span&gt;
ionosctl token delete --token-id &lt;span class="s2"&gt;"$TOKEN_ID"&lt;/span&gt;
ionosctl token delete --expired
ionosctl token delete --current &lt;span class="c"&gt;# el de IONOS_TOKEN&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Con 2FA activado, la referencia de la Auth API dice que el borrado sólo se puede hacer desde el Token Manager. Y hay otra forma de cortar el acceso sin tocar el token: sacar el data center del grupo (Revoke Access en Resources of Group). El token sigue vivo, pero deja de ver ese recurso.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lo que hace Control Cloud con ese token
&lt;/h2&gt;

&lt;p&gt;El formulario de Control Cloud ofrece token o usuario y contraseña, pero el inventario y las métricas de cada organización se leen con el token, así que es lo que conviene pegar. Lo guarda cifrado y lo usa contra la Cloud API v6 para listar data centers, servidores, LANs e IP blocks y dibujarlos en el mapa, y contra la Telemetry API para las métricas de un servidor cuando abrís su panel. Con el usuario de solo lectura de arriba el mapa funciona; prender o apagar servidores desde Control Cloud necesita Edit sobre ese data center. Un detalle que salió del uso: el código detecta un token pegado dos veces seguidas y se queda con uno. El paso a paso para conectarlo está en la &lt;a href="https://controlcloud.dev/guias/ionos" rel="noopener noreferrer"&gt;guía de IONOS Cloud&lt;/a&gt;; si querés probarlo con tu contrato, &lt;a href="https://controlcloud.dev/contact" rel="noopener noreferrer"&gt;pedí acceso&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>cloud</category>
      <category>devops</category>
      <category>security</category>
      <category>api</category>
    </item>
    <item>
      <title>Token de API da IONOS Cloud: o DCD não é o Control Center, quanto tempo dura e como deixá-lo somente leitura</title>
      <dc:creator>Gonzalo Gjuka</dc:creator>
      <pubDate>Thu, 12 Nov 2026 15:44:29 +0000</pubDate>
      <link>https://dev.to/gonzalogjuka/token-de-api-da-ionos-cloud-o-dcd-nao-e-o-control-center-quanto-tempo-dura-e-como-deixa-lo-2eih</link>
      <guid>https://dev.to/gonzalogjuka/token-de-api-da-ionos-cloud-o-dcd-nao-e-o-control-center-quanto-tempo-dura-e-como-deixa-lo-2eih</guid>
      <description>&lt;p&gt;O &lt;a href="https://controlcloud.dev/pt/guias/ionos" rel="noopener noreferrer"&gt;guia da IONOS Cloud&lt;/a&gt; deste site explica em quatro passos como gerar o token no DCD e colá-lo. O Search Console me mostrou que as pessoas chegam com perguntas que o guia não responde: "ionos controlcenter", "dcd ionos", "ionos api token". Juntei aqui: onde cada credencial da IONOS é criada, quanto o token dura, como montar um usuário que de fato só pode ler e como revogá-lo. Tudo sai da documentação da IONOS: docs.ionos.com, a referência da Auth API e a do ionosctl.&lt;/p&gt;

&lt;h2&gt;
  
  
  DCD, Control Center e Developer Portal: três lugares, duas credenciais
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;O DCD (Data Center Designer, dcd.ionos.com) é o console da IONOS Cloud: data centers virtuais, servidores, LANs, IP blocks. O token da Cloud API é gerado ali, em Menu → Management → Token Manager.&lt;/li&gt;
&lt;li&gt;O painel de cliente do my.ionos.com, o que muita gente procura como "IONOS Control Center", é onde os produtos são contratados. O token da Cloud API não é criado nesse painel: o Token Manager fica no DCD.&lt;/li&gt;
&lt;li&gt;O Developer Portal de hospedagem (developer.hosting.ionos.com) emite outra coisa: API keys com um public prefix e um secret, enviadas no header X-API-Key, depois de ativar o acesso à API de hospedagem pelo my.ionos.com. Segundo a própria documentação, essas APIs configuram produtos de hospedagem (domínios, DNS, SSL). Se você procurava um token para os servidores do DCD, essa key não é.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A Cloud API (&lt;a href="https://api.ionos.com/cloudapi/v6" rel="noopener noreferrer"&gt;https://api.ionos.com/cloudapi/v6&lt;/a&gt;) aceita o token como Authorization: Bearer, ou usuário e senha por Basic. O token é a opção que vence sozinha e pode ser revogada sem trocar a senha de ninguém.&lt;/p&gt;

&lt;h2&gt;
  
  
  Três formas de gerar o token, e quanto ele dura
&lt;/h2&gt;

&lt;p&gt;Pelo DCD: Menu → Management → Token Manager → Generate Token. O TTL é escolhido de uma lista (1 hora, 4 horas, 1, 7, 30, 60, 90, 180 ou 365 dias), e o valor do token aparece uma única vez; nesse momento dá para baixá-lo como arquivo de texto. Na lista ficam o ID, a data de criação e a de vencimento, nunca o valor.&lt;/p&gt;

&lt;p&gt;Pela API ou com o ionosctl, o TTL vai em segundos (ou como 30D na CLI), com mínimo de 60 segundos e máximo de um ano, que também é o padrão se você não pedir outro:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Auth API: um token de 30 dias (2.592.000 s) com usuário e senha&lt;/span&gt;
curl -s -u &lt;span class="s2"&gt;"$IONOS_USERNAME:$IONOS_PASSWORD"&lt;/span&gt; \
  &lt;span class="s2"&gt;"https://api.ionos.com/auth/v1/tokens/generate?ttl=2592000"&lt;/span&gt;

&lt;span class="c"&gt;# o mesmo com o ionosctl&lt;/span&gt;
ionosctl token generate --ttl 30D

&lt;span class="c"&gt;# quais tokens o usuário tem e quando cada um vence&lt;/span&gt;
ionosctl token list
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Dois limites que constam na referência da Auth API. A autenticação Basic desse endpoint só funciona se o usuário não tiver 2FA; com 2FA, o token é gerado no Token Manager do DCD. E há um teto de 100 tokens ativos por usuário, que um script que gera um novo a cada execução alcança rápido. ionosctl token delete --expired limpa os vencidos.&lt;/p&gt;

&lt;p&gt;Para uma integração que só lê, eu escolho 90 dias e anoto a data de vencimento: nesse dia a integração para de ler, e prefiro que isso aconteça com aviso. Um ano é o padrão da API, não uma recomendação.&lt;/p&gt;

&lt;h2&gt;
  
  
  Somente leitura: o token não tem escopos, o usuário tem limites
&lt;/h2&gt;

&lt;p&gt;O token não carrega permissões próprias: ele age com a identidade do usuário que o gerou. O dono do contrato e os administradores veem e podem modificar tudo (a página Users &amp;amp; Groups esclarece que administradores têm acesso a todos os recursos do contrato sem estar em nenhum grupo), então um token gerado pelo seu usuário administrador é um token de administrador, seja qual for o uso. Um token de leitura sai de um usuário que não é administrador: mínimo privilégio montado com o RBAC da IONOS, que a mesma página descreve como usuários, grupos, privilégios e recursos compartilhados. No DCD, Menu → Management → Users &amp;amp; Groups:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Users → Create: um usuário novo, com e-mail e senha, sem promovê-lo a administrador.&lt;/li&gt;
&lt;li&gt;Groups → Create: um grupo, e na aba Privileges você não marca nada. Privilégios são ações sobre o contrato (criar data centers, reservar IP blocks, criar snapshots ou clusters de Kubernetes); um grupo de leitura não precisa de nenhum.&lt;/li&gt;
&lt;li&gt;Members: adicione o usuário ao grupo.&lt;/li&gt;
&lt;li&gt;Resources of Group → Grant Access: cada data center e cada IP block que ele deve ver. Segundo a documentação, atribuir o recurso habilita a leitura; Edit e Share são caixas à parte, que ficam desmarcadas.&lt;/li&gt;
&lt;li&gt;Entre no DCD com esse usuário e gere o token no Token Manager dele. Esse é o token que vai em qualquer ferramenta de inventário.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Com o ionosctl é igual. A referência do share create diz que o share, por padrão, dá acesso de leitura e uso; editar é a flag --edit-privilege:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# grupo sem privilégios, com o usuário dentro&lt;/span&gt;
ionosctl compute group create --name &lt;span class="s2"&gt;"somente-leitura"&lt;/span&gt;
ionosctl compute group user add --group-id &lt;span class="s2"&gt;"$GROUP_ID"&lt;/span&gt; --user-id &lt;span class="s2"&gt;"$USER_ID"&lt;/span&gt;

&lt;span class="c"&gt;# IDs dos recursos que podem ser compartilhados&lt;/span&gt;
ionosctl compute resource list

&lt;span class="c"&gt;# um share por data center, sem --edit-privilege nem --share-privilege&lt;/span&gt;
ionosctl compute share create --group-id &lt;span class="s2"&gt;"$GROUP_ID"&lt;/span&gt; --resource-id &lt;span class="s2"&gt;"$DATACENTER_ID"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Antes de colá-lo em qualquer lugar, confira o que ele é. ionosctl token parse mostra o ID do token, o usuário, o contrato e o papel, e com --privileges os privilégios desse usuário. A Cloud API diz quais data centers ele vê:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ionosctl token parse --token &lt;span class="s2"&gt;"$IONOS_TOKEN"&lt;/span&gt;
ionosctl token parse --token &lt;span class="s2"&gt;"$IONOS_TOKEN"&lt;/span&gt; --privileges

&lt;span class="c"&gt;# data centers visíveis para este token&lt;/span&gt;
curl -s -H &lt;span class="s2"&gt;"Authorization: Bearer $IONOS_TOKEN"&lt;/span&gt; \
  &lt;span class="s2"&gt;"https://api.ionos.com/cloudapi/v6/datacenters?depth=1"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; jq -r &lt;span class="s1"&gt;'.items[].properties.name'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Se a lista vier vazia, o token está certo: a documentação avisa que os recursos criados pelo dono do contrato não ficam visíveis para outros usuários até serem compartilhados com o grupo deles. Falta o Grant Access desse data center.&lt;/p&gt;

&lt;p&gt;As métricas são um caso à parte. A referência do ionosctl descreve o privilégio "access and manage monitoring" como acesso a métricas e gestão de alarmes. Se você quer ver CPU e rede dos servidores com esse usuário, é o único privilégio que eu acrescentaria, sabendo que com ele o usuário deixa de ser somente leitura.&lt;/p&gt;

&lt;h2&gt;
  
  
  Revogar: Token Manager, API e o detalhe do 2FA
&lt;/h2&gt;

&lt;p&gt;No Token Manager, Delete invalida o token na hora, mesmo que não tenha vencido. Pela API e pela CLI se apaga por ID (DELETE /auth/v1/tokens/{tokenId}) ou por critério: CURRENT (o que você está usando), EXPIRED ou ALL.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ionosctl token list &lt;span class="c"&gt;# ID, criação e vencimento&lt;/span&gt;
ionosctl token delete --token-id &lt;span class="s2"&gt;"$TOKEN_ID"&lt;/span&gt;
ionosctl token delete --expired
ionosctl token delete --current &lt;span class="c"&gt;# o de IONOS_TOKEN&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Com 2FA ativado, a referência da Auth API diz que a exclusão só pode ser feita pelo Token Manager. E há outra forma de cortar o acesso sem mexer no token: tirar o data center do grupo (Revoke Access em Resources of Group). O token continua vivo, mas deixa de ver esse recurso.&lt;/p&gt;

&lt;h2&gt;
  
  
  O que o Control Cloud faz com esse token
&lt;/h2&gt;

&lt;p&gt;O formulário do Control Cloud oferece token ou usuário e senha, mas o inventário e as métricas de cada organização são lidos com o token, então é ele que convém colar. Ele fica guardado cifrado e é usado contra a Cloud API v6 para listar data centers, servidores, LANs e IP blocks e desenhá-los no mapa, e contra a Telemetry API para as métricas de um servidor quando você abre o painel dele. Com o usuário somente leitura acima o mapa funciona; ligar ou desligar servidores pelo Control Cloud precisa de Edit nesse data center. Um detalhe que saiu do uso: o código detecta um token colado duas vezes seguidas e fica com um. O passo a passo para conectar está no &lt;a href="https://controlcloud.dev/pt/guias/ionos" rel="noopener noreferrer"&gt;guia da IONOS Cloud&lt;/a&gt;; se quiser testar com o seu contrato, &lt;a href="https://controlcloud.dev/pt/contact" rel="noopener noreferrer"&gt;peça acesso&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>braziliandevs</category>
      <category>cloud</category>
      <category>devops</category>
      <category>security</category>
    </item>
    <item>
      <title>IONOS Cloud API token: the DCD is not the Control Center, how long it lasts and how to make it read-only</title>
      <dc:creator>Gonzalo Gjuka</dc:creator>
      <pubDate>Thu, 12 Nov 2026 15:44:16 +0000</pubDate>
      <link>https://dev.to/gonzalogjuka/ionos-cloud-api-token-the-dcd-is-not-the-control-center-how-long-it-lasts-and-how-to-make-it-4fd6</link>
      <guid>https://dev.to/gonzalogjuka/ionos-cloud-api-token-the-dcd-is-not-the-control-center-how-long-it-lasts-and-how-to-make-it-4fd6</guid>
      <description>&lt;p&gt;This site's &lt;a href="https://controlcloud.dev/en/guias/ionos" rel="noopener noreferrer"&gt;IONOS Cloud guide&lt;/a&gt; explains in four steps how to generate the token in the DCD and paste it. Search Console showed me people arrive with questions the guide does not answer: "ionos controlcenter", "dcd ionos", "ionos api token". I gathered them here: where each IONOS credential is created, how long the token lasts, how to build a user that really can only read, and how to revoke it. Everything comes from IONOS documentation: docs.ionos.com, the Auth API reference and the ionosctl reference.&lt;/p&gt;

&lt;h2&gt;
  
  
  DCD, Control Center and Developer Portal: three places, two credentials
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The DCD (Data Center Designer, dcd.ionos.com) is the IONOS Cloud console: virtual data centers, servers, LANs, IP blocks. The Cloud API token is generated there, under Menu → Management → Token Manager.&lt;/li&gt;
&lt;li&gt;The customer panel at my.ionos.com, the one many people search for as "IONOS Control Center", is where products are ordered. The Cloud API token is not created in that panel: the Token Manager lives in the DCD.&lt;/li&gt;
&lt;li&gt;The hosting Developer Portal (developer.hosting.ionos.com) issues something else: API keys with a public prefix and a secret, sent in the X-API-Key header, after you enable API access to hosting services from my.ionos.com. According to its own documentation, those APIs configure hosting products (domains, DNS, SSL). If you wanted a token for your DCD servers, that key is not it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Cloud API (&lt;a href="https://api.ionos.com/cloudapi/v6" rel="noopener noreferrer"&gt;https://api.ionos.com/cloudapi/v6&lt;/a&gt;) accepts the token as Authorization: Bearer, or a username and password over Basic. The token is the option that expires on its own and can be revoked without changing anyone's password.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three ways to generate the token, and how long it lasts
&lt;/h2&gt;

&lt;p&gt;From the DCD: Menu → Management → Token Manager → Generate Token. The TTL comes from a list (1 hour, 4 hours, 1, 7, 30, 60, 90, 180 or 365 days), and the token value is shown only once; you can download it as a text file at that moment. The list keeps the ID, the creation date and the expiration date, never the value.&lt;/p&gt;

&lt;p&gt;Through the API or ionosctl, the TTL is given in seconds (or as 30D in the CLI), with a minimum of 60 seconds and a maximum of one year, which is also the default if you do not ask for one:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Auth API: a 30-day token (2,592,000 s) with username and password&lt;/span&gt;
curl -s -u &lt;span class="s2"&gt;"$IONOS_USERNAME:$IONOS_PASSWORD"&lt;/span&gt; \
  &lt;span class="s2"&gt;"https://api.ionos.com/auth/v1/tokens/generate?ttl=2592000"&lt;/span&gt;

&lt;span class="c"&gt;# the same with ionosctl&lt;/span&gt;
ionosctl token generate --ttl 30D

&lt;span class="c"&gt;# which tokens the user has and when each one expires&lt;/span&gt;
ionosctl token list
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two limits stated in the Auth API reference. Basic authentication on that endpoint only works if the user has no 2FA; with 2FA, the token is generated in the DCD Token Manager. And there is a cap of 100 active tokens per user, which a script that generates a new one on every run reaches quickly. ionosctl token delete --expired clears the expired ones.&lt;/p&gt;

&lt;p&gt;For an integration that only reads, I pick 90 days and write down the expiration date: that day the integration stops reading, and I would rather it happen with a heads-up. One year is the API default, not a recommendation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Read-only: the token has no scopes, the user has limits
&lt;/h2&gt;

&lt;p&gt;The token carries no permissions of its own: it acts with the identity of the user who generated it. The contract owner and administrators can see and modify everything (the Users &amp;amp; Groups page notes that administrators have access to every resource in the contract without being in any group), so a token generated from your administrator user is an administrator token, whatever you use it for. A read token comes from a user who is not an administrator: least privilege built with IONOS RBAC, which the same page describes as users, groups, privileges and shared resources. In the DCD, Menu → Management → Users &amp;amp; Groups:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Users → Create: a new user, with an email and password, not promoted to administrator.&lt;/li&gt;
&lt;li&gt;Groups → Create: a group, and on the Privileges tab you tick nothing. Privileges are contract-wide actions (create data centers, reserve IP blocks, create snapshots or Kubernetes clusters); a read group needs none of them.&lt;/li&gt;
&lt;li&gt;Members: add the user to the group.&lt;/li&gt;
&lt;li&gt;Resources of Group → Grant Access: every data center and IP block it should see. According to the documentation, assigning the resource enables read access; Edit and Share are separate checkboxes that stay unticked.&lt;/li&gt;
&lt;li&gt;Log in to the DCD as that user and generate the token in its Token Manager. That is the token that goes into any inventory tool.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With ionosctl it is the same. The share create reference says a share grants read/use access by default; editing is the --edit-privilege flag:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# a group with no privileges, with the user in it&lt;/span&gt;
ionosctl compute group create --name &lt;span class="s2"&gt;"read-only"&lt;/span&gt;
ionosctl compute group user add --group-id &lt;span class="s2"&gt;"$GROUP_ID"&lt;/span&gt; --user-id &lt;span class="s2"&gt;"$USER_ID"&lt;/span&gt;

&lt;span class="c"&gt;# IDs of the resources that can be shared&lt;/span&gt;
ionosctl compute resource list

&lt;span class="c"&gt;# one share per data center, without --edit-privilege or --share-privilege&lt;/span&gt;
ionosctl compute share create --group-id &lt;span class="s2"&gt;"$GROUP_ID"&lt;/span&gt; --resource-id &lt;span class="s2"&gt;"$DATACENTER_ID"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Before pasting it anywhere, check what it is. ionosctl token parse shows the token ID, the user, the contract and the role, and with --privileges that user's privileges. The Cloud API tells you which data centers it sees:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ionosctl token parse --token &lt;span class="s2"&gt;"$IONOS_TOKEN"&lt;/span&gt;
ionosctl token parse --token &lt;span class="s2"&gt;"$IONOS_TOKEN"&lt;/span&gt; --privileges

&lt;span class="c"&gt;# data centers visible to this token&lt;/span&gt;
curl -s -H &lt;span class="s2"&gt;"Authorization: Bearer $IONOS_TOKEN"&lt;/span&gt; \
  &lt;span class="s2"&gt;"https://api.ionos.com/cloudapi/v6/datacenters?depth=1"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; jq -r &lt;span class="s1"&gt;'.items[].properties.name'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the list comes back empty, the token is fine: the documentation warns that resources created by the contract owner are not visible to other users until they are shared with their group. The Grant Access for that data center is missing.&lt;/p&gt;

&lt;p&gt;Metrics are a separate case. The ionosctl reference describes the "access and manage monitoring" privilege as access to metrics and alarm management. If you want to see the servers' CPU and network with that user, it is the only privilege I would add, knowing that with it the user is no longer purely read-only.&lt;/p&gt;

&lt;h2&gt;
  
  
  Revoking: Token Manager, API and the 2FA detail
&lt;/h2&gt;

&lt;p&gt;In the Token Manager, Delete invalidates the token immediately, even if it has not expired. Through the API and the CLI you delete by ID (DELETE /auth/v1/tokens/{tokenId}) or by criteria: CURRENT (the one you are using), EXPIRED or ALL.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ionosctl token list &lt;span class="c"&gt;# ID, creation and expiration&lt;/span&gt;
ionosctl token delete --token-id &lt;span class="s2"&gt;"$TOKEN_ID"&lt;/span&gt;
ionosctl token delete --expired
ionosctl token delete --current &lt;span class="c"&gt;# the one in IONOS_TOKEN&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With 2FA enabled, the Auth API reference says deletion can only be done from the Token Manager. And there is another way to cut access without touching the token: remove the data center from the group (Revoke Access in Resources of Group). The token stays alive, but it stops seeing that resource.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Control Cloud does with that token
&lt;/h2&gt;

&lt;p&gt;The Control Cloud form offers a token or a username and password, but each organization's inventory and metrics are read with the token, so that is what to paste. It is stored encrypted and used against Cloud API v6 to list data centers, servers, LANs and IP blocks and draw them on the map, and against the Telemetry API for a server's metrics when you open its panel. With the read-only user above the map works; starting or stopping servers from Control Cloud needs Edit on that data center. One detail that came from real use: the code detects a token pasted twice in a row and keeps one. The step-by-step to connect it is in the &lt;a href="https://controlcloud.dev/en/guias/ionos" rel="noopener noreferrer"&gt;IONOS Cloud guide&lt;/a&gt;; if you want to try it with your contract, &lt;a href="https://controlcloud.dev/en/contact" rel="noopener noreferrer"&gt;request access&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>cloud</category>
      <category>devops</category>
      <category>security</category>
      <category>api</category>
    </item>
  </channel>
</rss>
