Hi ha un problema a Contoso que cap de les lliçons anteriors no ha tocat. La classe Reserva, amb la seva validació del localitzador, les seves regles de dates i el seu càlcul de places, està copiada i enganxada en tres repositoris: a contoso-reservas, a contoso-api-disponibilidad i al procés nocturn que genera les targetes d'embarcament. Van néixer iguals, però cadascuna s'ha anat tocant pel seu compte i avui hi ha tres versions diferents. La conseqüència es veu en producció: el web accepta un localitzador de set caràcters que l'API rebutja, i ningú no sap quina de les tres té raó.

Copiar codi compartit no escala. La solució és tractar-lo com el que és —una biblioteca amb versió pròpia— i distribuir-lo com un paquet. Azure Artifacts és el servei d'Azure DevOps que allotja aquests paquets de manera privada, i de passada resol un problema més gros del qual potser no eres conscient: la seguretat de la teva cadena de subministrament de dependències.

Contingut

  1. Gestors de paquets i feeds
  2. Tipus de paquet admesos i Universal Packages
  3. Crear el feed contoso-paquetes: àmbit i vistes
  4. Orígens ascendents i la confusió de dependències
  5. Publicar i consumir contoso.reservas.modelos
  6. Versionat semàntic automatitzat des de la canalització
  7. Retenció, neteja i cost
  8. Seguretat de la cadena de subministrament
  9. Comparació amb GitHub Packages i amb un registre de contenidors
  10. Errors Comuns i Consells
  11. Exercicis
  12. Conclusió

  1. Gestors de paquets i feeds

Un gestor de paquets (NuGet, npm, Maven, pip) fa tres coses: descarrega una biblioteca i les seves dependències transitives, resol les versions compatibles entre totes elles i deixa constància del que s'ha fet servir. Un feed és un repositori de paquets: el lloc del qual el gestor descarrega i al qual es publica.

Davant de copiar i enganxar, un paquet aporta tres garanties que Contoso necessita:

Codi copiat Paquet en un feed
Identitat Cap: tres còpies sense nom comú Nom i versió explícits
Actualització Manual, repositori per repositori dotnet add package i un número que puja
Saber qui fa servir què Impossible La llista de consumidors del feed
Canvis incompatibles Es descobreixen en producció S'anuncien pujant la versió major

  1. Tipus de paquet admesos i Universal Packages

Tipus Ecosistema Ús típic a Contoso
NuGet .NET contoso.reservas.modelos, la biblioteca compartida
npm JavaScript / TypeScript Components web comuns del portal
Maven Java Integració amb el sistema heretat de tripulacions
Python Python Utilitats del procés analític de 03-06
Cargo Rust Avui no es fa servir
Universal Packages Qualsevol cosa Fitxers que no són paquets de codi

Els Universal Packages mereixen explicació perquè resolen un cas molt real. No tot el que cal versionar i compartir és una biblioteca: Contoso té un conjunt de dades d'aeroports i rutes de 300 MB, plantilles de targeta d'embarcament en PDF i fitxers de configuració de la passarel·la de pagament. Res d'això no encaixa a NuGet, no ha d'anar al repositori Git (05-02) i tanmateix necessita versió, historial i traçabilitat. Un Universal Package és simplement una carpeta versionada, publicada i descarregada amb la CLI:

# Publicar la carpeta de dades d'aeroports com a paquet universal
az artifacts universal publish \
  --organization https://dev.azure.com/contoso-airlines \
  --feed contoso-paquetes --name datos-aeropuertos --version 1.4.0 \
  --path ./dades --description "Cataleg d aeroports i rutes"

# Consumir-lo des d'una canalitzacio o des d'una maquina
az artifacts universal download \
  --organization https://dev.azure.com/contoso-airlines \
  --feed contoso-paquetes --name datos-aeropuertos --version 1.4.0 --path ./dades

  1. Crear el feed contoso-paquetes: àmbit i vistes

# El feed es crea des del portal (Artifacts) o amb l'API REST.
# Ambit d'organitzacio: visible des de qualsevol projecte de contoso-airlines.
az artifacts universal publish --help   # La CLI cobreix publicacio i descarrega

La primera decisió és l'àmbit:

Àmbit de projecte Àmbit d'organització
Qui el veu Només el projecte que el conté Qualsevol projecte de l'organització
Permisos Heretats del projecte Es gestionen a part
Quan triar-lo Paquets interns d'un producte Biblioteques compartides entre productes

Contoso crea contoso-paquetes amb àmbit d'organització, perquè contoso.reservas.modelos també la consumirà el projecte contoso-millas quan existeixi. Un feed d'àmbit de projecte obligaria a duplicar-lo.

La segona decisió són les vistes, que són el mecanisme de promoció. Un feed nou en porta tres:

Vista Què conté Qui hi consumeix
@local Tot el que s'hi ha publicat, més el que s'ha descarregat d'orígens ascendents Les mateixes canalitzacions de compilació
@prerelease Versions promocionades per provar Entorns de desenvolupament i preproducció
@release Versions aprovades per a producció Les canalitzacions que despleguen en producció

El flux és el mateix patró de portes de 05-04, aplicat als paquets: cada compilació publica a @local, es promociona a @prerelease quan passa les proves d'integració, i a @release només quan es valida. Els consumidors apunten a la vista que els correspon mitjançant la URL del feed, que inclou el nom de la vista:

https://pkgs.dev.azure.com/contoso-airlines/_packaging/contoso-paquetes@release/nuget/v3/index.json

Una versió publicada no es modifica mai: la promoció no canvia el paquet, només el fa visible en una altra vista. Aquesta immutabilitat és el que permet afirmar que la versió 2.3.1 és la mateixa a tot arreu.

  1. Orígens ascendents i la confusió de dependències

Un origen ascendent (upstream source) fa que el teu feed actuï com a intermediari del registre públic: quan algú demana Newtonsoft.Json, el feed el busca primer entre els seus propis paquets, i si no el té el descarrega de nuget.org, en guarda una còpia i la serveix. Aporta dues coses:

  • Una única URL a la configuració de tots els projectes, en lloc de dues.
  • Protecció davant la desaparició d'un paquet. Si un autor retira la seva biblioteca del registre públic —ha passat, i ha trencat milers de compilacions—, la còpia guardada al teu feed hi continua sent i les teves canalitzacions continuen compilant.

Però la raó més important és de seguretat, i convé entendre-la bé.

La confusió de dependències

És un atac real de cadena de subministrament, demostrat el 2021 contra desenes de grans empreses. Funciona així:

  1. Contoso té un paquet intern anomenat contoso.reservas.modelos, que existeix només al seu feed privat.
  2. Un atacant descobreix aquest nom —apareix en un package.json filtrat, en una captura de pantalla d'una xerrada, en un missatge d'error públic— i publica al registre públic un paquet amb el mateix nom i una versió altíssima, 99.0.0.
  3. Si el gestor de paquets està configurat amb dos orígens —el feed privat i el públic— moltes implementacions consulten tots dos i trien la versió més alta. La versió més alta és la de l'atacant.
  4. La canalització de Contoso descarrega i executa codi de l'atacant durant la compilació, amb accés a les variables de l'entorn i a la xarxa de l'agent.
graph TD
    A["La canalitzacio demana<br/>contoso.reservas.modelos"] --> B{"Quants origens<br/>hi ha configurats?"}
    B -->|"Dos origens:<br/>privat i public"| C["Compara versions:<br/>2.3.1 privada vs 99.0.0 publica"]
    C --> D["Descarrega la 99.0.0<br/>de l atacant"]
    B -->|"Un sol origen:<br/>feed amb upstream"| E["El feed resol primer<br/>els seus paquets interns"]
    E --> F["Descarrega la 2.3.1<br/>legitima"]

La defensa que dóna Azure Artifacts és doble. Primer, un únic origen configurat: el feed, amb el registre públic com a origen ascendent, de manera que la resolució la decideix el feed i els paquets interns sempre guanyen. Segon, el feed marca els paquets que ha guardat des d'un origen ascendent i impedeix que un paquet públic suplanti un d'intern del mateix nom. A això s'hi afegeixen dues bones pràctiques: fer servir un prefix o espai de noms reservat per als teus paquets, i no configurar mai diversos orígens en paral·lel al nuget.config o al .npmrc.

  1. Publicar i consumir contoso.reservas.modelos

El repositori contoso-modelos deixa de ser una carpeta copiada i passa a tenir la seva pròpia canalització de publicació:

name: 2.3.$(Rev:r)        # MAJOR.MENOR fixades a ma; PEDAC automatic

trigger:
  branches: { include: [ main ] }

pool: { vmImage: ubuntu-latest }

variables:
  - name: feed
    value: 'contoso-airlines/contoso-paquetes'   # organitzacio/feed

steps:
  - task: UseDotNet@2
    inputs: { packageType: sdk, version: '8.0.x' }

  # Autenticacio contra el feed: obte un testimoni de l execucio, sense secrets
  - task: NuGetAuthenticate@1

  - script: dotnet build src/Contoso.Reservas.Modelos -c Release
  - script: dotnet test src/Contoso.Reservas.Modelos.Pruebas -c Release

  # El numero de versio del paquet es pren del numero de compilacio:
  # una versio per compilacio, sense que ningu editi un fitxer a ma
  - script: |
      dotnet pack src/Contoso.Reservas.Modelos \
        -c Release -o $(Build.ArtifactStagingDirectory) \
        -p:PackageVersion=$(Build.BuildNumber)
    displayName: Empaquetar

  # Publicar a la vista @local del feed
  - task: NuGetCommand@2
    inputs:
      command: push
      packagesToPush: '$(Build.ArtifactStagingDirectory)/*.nupkg'
      nuGetFeedType: internal
      publishVstsFeed: '$(feed)'
    displayName: Publicar a contoso-paquetes

NuGetAuthenticate@1 és la peça clau de seguretat: obté credencials del mateix context de l'execució, així que no hi ha cap testimoni d'accés personal guardat enlloc. La mateixa lògica d'identitat sense secrets del mòdul 4.

Del costat del consumidor, a contoso-reservas, el nuget.config declara un sol origen:

<configuration>
  <packageSources>
    <clear />   <!-- Imprescindible: elimina nuget.org heretat de la maquina -->
    <add key="contoso-paquetes"
         value="https://pkgs.dev.azure.com/contoso-airlines/_packaging/contoso-paquetes@release/nuget/v3/index.json" />
  </packageSources>
</configuration>

El <clear /> no és decoratiu: sense ell, l'origen públic global de la màquina continua actiu i es torna a obrir la porta a la confusió de dependències.

  1. Versionat semàntic automatitzat des de la canalització

Les regles de 05-02 s'apliquen igual als paquets, amb una conseqüència contractual: la versió és una promesa a qui consumeix.

Canvi a contoso.reservas.modelos Versió
S'afegeix una propietat opcional a Reserva 2.3.1 → 2.4.0 (menor)
Es corregeix la validació del localitzador sense canviar la signatura 2.3.1 → 2.3.2 (pedaç)
Es reanomena CodigoReserva a Localizador 2.3.1 → 3.0.0 (major)
Publicació de prova abans de consolidar 2.4.0-preview.5

L'automatisme consisteix a derivar el número de la canalització. Amb name: 2.3.$(Rev:r) i -p:PackageVersion=$(Build.BuildNumber), cada compilació de main produeix una versió nova sense que ningú editi cap fitxer: les parts major i menor les decideix una persona en canviar el name —perquè són una decisió de disseny—, i el pedaç el porta la màquina. Les versions de prova es marquen amb sufix (-preview.N), i els gestors de paquets les ignoren per defecte, cosa que les fa ideals per a la vista @prerelease.

  1. Retenció, neteja i cost

Avís de cost: Azure Artifacts factura per emmagatzematge total del feed, amb 2 GiB gratuïts per organització i un cost creixent per damunt (de l'ordre de 2 $/GiB al mes al primer tram). Sembla poc fins que una canalització publica una versió a cada compilació: 20 compilacions diàries × 30 MB són 600 MB al mes, i en mig any t'has menjat la quota. A més, els paquets guardats des d'orígens ascendents també compten, igual que l'emmagatzematge de Git LFS de 05-02.

La defensa és una directiva de retenció per feed: conservar les N versions més recents de cada paquet i eliminar automàticament la resta passats X dies. Dues precaucions en configurar-la: els paquets promocionats a una vista no s'eliminen mai per retenció —que és exactament el motiu pel qual es promociona el que importa—, i convé revisar el consum periòdicament a la vista de facturació, perquè el creixement és silenciós.

  1. Seguretat de la cadena de subministrament

Les teves dependències són codi de tercers que s'executa amb els teus permisos. Cinc pràctiques, de més elemental a més avançada:

  1. Fixar versions i fer servir fitxers de bloqueig. packages.lock.json a .NET, package-lock.json a npm: registren la versió exacta de cada dependència, incloses les transitives, i fan la compilació reproduïble. Sense bloqueig, la mateixa confirmació pot compilar diferent demà. Ha d'estar confirmat al repositori.
  2. Analitzar vulnerabilitats. dotnet list package --vulnerable --include-transitive a la canalització de 05-03, i eines d'anàlisi de composició que creuen les teves dependències amb les bases de dades de CVE. El transitiu importa: gairebé sempre la vulnerabilitat és tres nivells per sota del que tu vas declarar.
  3. Evitar la confusió de dependències amb un sol origen i <clear />, tal com s'ha vist.
  4. Generar una llista de materials de programari (SBOM), l'inventari complet de tot el que compon el teu artefacte. Quan apareix una vulnerabilitat greu en una biblioteca —el cas de Log4Shell n'és l'exemple canònic—, la pregunta «estem afectats?» es respon consultant l'SBOM en minuts en lloc d'auditant repositoris durant dies. Es genera a la canalització i es publica al costat de l'artefacte.
  5. Signar els paquets, perquè qui els consumeix pugui verificar que provenen de Contoso i no han estat alterats. El certificat viu a kv-contoso-pro (04-03) i la signatura es fa a la canalització, mai en un portàtil.

  1. Comparació amb GitHub Packages i amb un registre de contenidors

Azure Artifacts GitHub Packages Azure Container Registry
Què allotja NuGet, npm, Maven, Python, Cargo, universals Els mateixos, més contenidors Imatges de contenidor i artefactes OCI
Orígens ascendents Molt bons, amb protecció de suplantació Limitats Memòria cau de registres públics
Vistes i promoció Sí (@local, @prerelease, @release) No, es fan servir etiquetes Etiquetes i bloqueig d'imatge
Facturació Per emmagatzematge, 2 GiB gratuïts Per emmagatzematge i transferència Per nivell del registre

La distinció amb un registre de contenidors és conceptual i convé tenir-la clara: un feed de paquets distribueix peces per construir una aplicació —biblioteques que es compilen dins del teu binari—, mentre que un registre de contenidors distribueix l'aplicació sencera ja construïda, amb el seu sistema de fitxers i les seves dependències del sistema operatiu. No competeixen: la canalització consumeix paquets d'Artifacts per produir una imatge que publica al registre. Aquest registre, Azure Container Registry, és justament on arrenca la lliçó 06-01, i tornarà a aparèixer a 05-06 com a magatzem de mòduls de Bicep.

Errors Comuns i Consells

  • Copiar i enganxar la biblioteca compartida. És el problema amb què ha començat la lliçó: tres còpies, tres veritats i una errada en producció.
  • Configurar el feed privat i el públic en paral·lel. És la porta oberta a la confusió de dependències. Un sol origen, amb orígens ascendents, i <clear /> per eliminar els heretats.
  • Guardar un testimoni d'accés personal per publicar. Innecessari i perillós: NuGetAuthenticate@1 (o el seu equivalent per a npm i Maven) fa servir la identitat de l'execució.
  • Publicar sense directiva de retenció. El feed creix en silenci fins que arriba la factura. Configura-la el mateix dia que crees el feed.
  • Editar el número de versió a mà. S'oblida, es duplica i bloqueja la publicació. Deriva'l de Build.BuildNumber.
  • Pujar la versió major sense avisar. El versionat semàntic és un contracte: un canvi incompatible publicat com a pedaç trenca qui consumeix sense previ avís.
  • Ignorar les dependències transitives. És on viu la majoria de les vulnerabilitats. --include-transitive, sempre.
  • Consell: reserva un prefix per als teus paquets (contoso.*) i documenta que aquest espai de noms és intern. Facilita detectar suplantacions.
  • Consell: confirma el fitxer de bloqueig al repositori i tracta'l com a codi: si canvia en una sol·licitud de canvis, algú ha de mirar per què.

Exercicis

Exercici 1: dissenyar el feed

Contoso Millas compartirà dues coses amb el projecte de reserves: una biblioteca .NET de càlcul de punts i un conjunt de dades de 200 MB amb les taules de bescanvi, que canvia mensualment.

  1. Un feed nou o el mateix contoso-paquetes? Amb quin àmbit?
  2. Quin tipus de paquet correspon a cadascun dels dos elements i per què?
  3. Estima el creixement anual d'emmagatzematge del conjunt de dades i proposa una directiva de retenció.

Exercici 2: confusió de dependències

Un desenvolupador de Contoso publica en un fòrum públic una traça d'error que inclou la línia Contoso.Reservas.Modelos, Version=2.3.1. Dues setmanes després, la canalització de l'API comença a descarregar contoso.reservas.modelos 99.0.0, un paquet que ningú de l'equip no ha publicat.

  1. Explica el mecanisme de l'atac pas a pas.
  2. Revisa aquest nuget.config i indica què hi falla: <packageSources><add key="nuget.org" value="https://api.nuget.org/v3/index.json" /><add key="contoso" value="https://pkgs.dev.azure.com/.../contoso-paquetes@release/nuget/v3/index.json" /></packageSources>
  3. Quines tres mesures aplicaries i quina és la més important?

Exercici 3: versionat i promoció

La biblioteca contoso.reservas.modelos està a la versió 2.3.1. En Diego necessita: (a) corregir la validació del localitzador sense canviar cap signatura pública; (b) afegir una propietat opcional AsientoPreferente; (c) reanomenar CodigoReserva a Localizador.

  1. Assigna número de versió a cada canvi i justifica'l.
  2. Descriu el recorregut del canvi (c) per les vistes del feed fins a arribar a producció.
  3. Què ha de passar a contoso-reservas i contoso-api-disponibilidad per consumir (c), i com s'evita trencar-los alhora?

Solucions

Solució 1:

  1. El mateix contoso-paquetes, amb àmbit d'organització, que és justament per a això que es va triar aquest àmbit: permet que dos projectes diferents consumeixin les mateixes biblioteques sense duplicar feeds ni configuració. Un feed per projecte només tindria sentit si els permisos haguessin d'estar taxativament separats.
  2. La biblioteca de càlcul de punts, paquet NuGet: és codi .NET que es compila dins de les aplicacions consumidores i necessita resolució de dependències. Les taules de bescanvi, Universal Package: són dades, no codi, no encaixen en cap gestor de paquets, no han d'anar al repositori Git per la seva mida i tot i així necessiten versió i traçabilitat.
  3. 200 MB × 12 publicacions a l'any = 2,4 GiB anuals, que per si sols superen la quota gratuïta de 2 GiB de tota l'organització. Directiva raonable: conservar les 3 versions més recents i eliminar la resta passats 90 dies, promocionant a @release la versió en ús en producció per protegir-la de l'esborrat automàtic. Així el consum s'estabilitza al voltant de 600-800 MB.

Solució 2:

  1. L'atacant obté el nom intern del paquet des de la traça pública. Publica a nuget.org un paquet homònim amb versió 99.0.0. La canalització de l'API té configurats dos orígens en paral·lel, així que consulta tots dos i tria la versió més alta, que és la de l'atacant. Descarrega aquest paquet i executa el seu codi durant la compilació, amb accés a les variables de l'agent i a la xarxa. A partir d'aquí l'atacant pot exfiltrar testimonis o alterar l'artefacte que es desplegarà.
  2. Falla en dues coses: declara dos orígens en paral·lel —el públic i el privat—, que és la condició necessària de l'atac; i no porta <clear />, així que a més arrossega els orígens globals configurats a la màquina o a la imatge de l'agent. El correcte és <clear /> seguit d'un únic origen, el feed de Contoso, amb nuget.org configurat com a origen ascendent del mateix feed.
  3. (a) Un sol origen amb <clear /> i orígens ascendents —la més important, perquè elimina l'ambigüitat de resolució que fa possible l'atac—. (b) Reservar i documentar el prefix contoso.* com a espai de noms intern. (c) Fitxer de bloqueig confirmat i anàlisi de dependències a la canalització, que hauria detectat el salt de versió anòmal. A més, rotar qualsevol credencial a la qual l'agent hagués tingut accés durant les compilacions compromeses.

Solució 3:

  1. (a) 2.3.2, pedaç: corregeix comportament sense alterar la superfície pública. (b) 2.4.0, menor: afegeix funcionalitat de manera compatible; qui no faci servir la propietat nova no se n'assabenta. (c) 3.0.0, major: reanomenar un membre públic és un canvi incompatible i tot el codi consumidor deixa de compilar.
  2. La canalització de contoso-modelos publica 3.0.0 a @local. Es promociona a @prerelease —possiblement abans com a 3.0.0-preview.1— i els entorns de desenvolupament la consumeixen i validen. Quan el web i l'API han migrat i les seves proves d'integració passen, es promociona a @release, que és la vista que consumeixen les canalitzacions de producció. La versió no es modifica en cap moment: només canvia on és visible.
  3. Cada repositori ha d'actualitzar la seva referència a 3.0.0 i adaptar el codi al nom nou, cadascun a la seva pròpia sol·licitud de canvis amb la seva revisió. Com que el consum es fa per versió explícita, no es trenquen alhora: mentre l'API continuï referenciant 2.4.0, continua compilant i desplegant amb normalitat. Aquest és precisament el valor del versionat davant del codi copiat, on el canvi hauria arribat a tothom de cop. Per suavitzar-ho encara més, una versió intermèdia 2.5.0 pot introduir Localizador marcant CodigoReserva com a obsolet, donant marge per migrar abans que la 3.0.0 l'elimini.

Conclusió

La biblioteca de models de Contoso ha deixat d'estar copiada en tres llocs. Entens què aporta un gestor de paquets davant de copiar i enganxar —identitat, versió, actualització traçable i un contracte explícit sobre els canvis incompatibles—, coneixes els tipus que allotja Azure Artifacts i saps quan un Universal Package és la resposta correcta: quan el que cal versionar i compartir no és codi i no cap al repositori Git, com el catàleg d'aeroports de Contoso. Has creat el feed contoso-paquetes amb àmbit d'organització perquè el pugui consumir també contoso-millas, i has fet servir les vistes @local, @prerelease i @release com a mecanisme de promoció —el mateix patró de portes de 05-04 aplicat als paquets—, sabent que una versió publicada és immutable i que promocionar només canvia on és visible.

Has entès a fons els orígens ascendents: la URL única, la còpia guardada que et protegeix si un paquet públic desapareix i, sobretot, la defensa contra la confusió de dependències, aquell atac de cadena de subministrament en què un paquet públic homònim amb versió altíssima suplanta l'intern i aconsegueix executar codi al teu agent de compilació. La defensa es resumeix en una línia de configuració: <clear /> i un únic origen. Has publicat contoso.reservas.modelos des d'una canalització que s'autentica amb NuGetAuthenticate@1 —sense cap testimoni guardat— i que deriva el número de versió de Build.BuildNumber, deixant a les persones només la decisió de disseny de quan puja la part major o menor. I has tancat amb el que sosté tota la resta: retenció configurada des del primer dia, perquè el feed creix en silenci sobre una quota gratuïta de 2 GiB, i les cinc pràctiques de seguretat de la cadena de subministrament —fixar versions amb fitxers de bloqueig, analitzar vulnerabilitats incloent-hi les transitives, un sol origen, generar l'SBOM que respon en minuts a «estem afectats?» i signar amb el certificat de kv-contoso-pro—.

Amb això, el cicle de lliurament de Contoso és gairebé complet: la feina es planifica a Boards, el codi es versiona i es revisa a Repos, es compila i es prova a Pipelines, es desplega amb aprovacions i ranures, i les peces compartides es distribueixen amb versió des d'Artifacts. Gairebé. Perquè tota la plataforma sobre la qual es desplega —les xarxes, les aplicacions, les bases de dades, les polítiques de governança del mòdul 4— continua existint únicament perquè algú va teclejar una vegada les ordres correctes. No hi ha manera de recrear-la, ni de saber qui va canviar què, ni d'aixecar-la a North Europe després d'un desastre. A l'última lliçó del mòdul, Infraestructura com a codi amb Bicep, això s'acaba: la plataforma sencera passarà a ser codi revisable, versionat i repetible, desplegat per la seva pròpia canalització amb vista prèvia, aprovació i tot el que has après aquí.

Curs d'Azure

Mòdul 1: Introducció a Azure

Mòdul 2: Serveis principals d'Azure

Mòdul 3: Bases de dades d'Azure

Mòdul 4: Seguretat a Azure

Mòdul 5: Azure DevOps

Mòdul 6: Serveis avançats d'Azure

Mòdul 7: Monitoratge i gestió

Mòdul 8: Gestió i optimització de costos

Mòdul 9: Estudis de cas i millors pràctiques

© Copyright 2026. Tots els drets reservats