Skip to content

DevOps

Clonar un Repositorio de GitHub de una organización a otra: Guía Paso a Paso

Resumen

Clonar un repositorio de GitHub entre organizaciones es útil cuando no puedes (o no te conviene) transferir el ownership. Con el enfoque correcto, duplicas todo el historial, ramas y tags en un repo nuevo sin romper nada en el origen.

¿Cuándo clonar y no transferir?

  • Transferir no es posible por políticas de la organización o compliance.
  • Necesitas mantener el repo original activo (p. ej., hard fork interno).
  • Quieres validar/limitar qué migra (código e historial sí; issues/PRs no).

Requisitos

  • Permisos de lectura en el repo origen y de escritura en el repo destino.
  • Repo destino creado y vacío en la organización de destino.
  • Autenticación configurada:
  • SSH: tener clave cargada en GitHub.
  • HTTPS: usar PAT con permisos repo para push.
  • Opcional: gh CLI para crear el repo destino desde terminal.

Warning

git push --mirror sobrescribe refs del remoto destino. Úsalo solo hacia un repo nuevo/vacío o cuando estés seguro de querer reemplazarlo por completo.

Pasos (SSH) — Opción rápida

Este flujo parte de un repo público origen y un repo vacío destino. Define primero las variables y luego ejecuta los comandos.

# Variables
ORG_SOURCE="source-org"
ORG_DEST="dest-org"
REPO_NAME="my-repo"

git clone git@github.com:$ORG_SOURCE/$REPO_NAME
cd $REPO_NAME
git remote rename origin source
git remote add origin git@github.com:$ORG_DEST/$REPO_NAME
git push --mirror origin
git remote remove source

Pasos (HTTPS) — Alternativa con PAT

# Variables
ORG_SOURCE="source-org"
ORG_DEST="dest-org"
REPO_NAME="my-repo"

git clone https://github.com/$ORG_SOURCE/$REPO_NAME.git
cd $REPO_NAME
git remote rename origin source
git remote add origin https://github.com/$ORG_DEST/$REPO_NAME.git
git push --mirror origin
git remote remove source

Al hacer push, Git solicitará usuario/token del PAT con permisos repo.

Opción recomendada (mirror real) — Bare clone

Para evitar empujar refs de seguimiento remotas innecesarias, puedes usar un espejo bare. Es el patrón que recomienda GitHub para duplicar repos.

# Variables
ORG_SOURCE="source-org"
ORG_DEST="dest-org"
REPO_NAME="my-repo"

# 1) Crear un mirror local (bare)
git clone --mirror git@github.com:$ORG_SOURCE/$REPO_NAME

cd $REPO_NAME.git

# 2) Añadir el remoto destino (repo vacío)
git remote add mirror git@github.com:$ORG_DEST/$REPO_NAME

# 3) Empujar todas las refs (branches, tags, notes)
git push --mirror mirror

Crear el repo destino (rápido con gh CLI)

Si aún no existe el repo en la organización destino:

# Autentícate si es necesario
gh auth login

# Variables
ORG_DEST="dest-org"
REPO_NAME="my-repo"

# Crea el repo vacío en la organización de destino
gh repo create "$ORG_DEST/$REPO_NAME" --private --confirm

Validaciones rápidas

  • Verifica ramas y tags en el remoto destino:
git ls-remote --heads origin
git ls-remote --tags origin
  • Abre el repo destino en GitHub y comprueba default branch, tags y commits.

Qué NO migra con este método

  • Issues, PRs, Discussions, Projects, Releases, Wikis y Secrets.
  • Para migrarlos, usa herramientas específicas (GitHub API/gh extensions) o realiza export/import manual según el caso.

Limpieza y siguientes pasos

  • Configura reglas de protección de rama en el repo destino.
  • Revisa y recrea Secrets, variables de entorno y Webhooks.
  • Actualiza pipelines (CI/CD) y badges que apunten al repo nuevo.
  • Considera archivar el repo origen si ya no se pretende usar.

Buenas prácticas

  • Usa SSH para entornos corporativos; HTTPS+PAT para automatizaciones.
  • Documenta la operación (fecha, responsables, commit de verificación).
  • Repite el proceso en un repo de prueba antes de hacerlo en producción.
  • Si el repo usa Git LFS, verifica que los objetos LFS estén correctamente en el destino.

Referencias

  • Duplicar un repositorio: https://docs.github.com/en/repositories/creating-and-managing-repositories/duplicating-a-repository
  • Transferir un repositorio: https://docs.github.com/en/repositories/creating-and-managing-repositories/transferring-a-repository
  • Autenticación SSH en GitHub: https://docs.github.com/en/authentication/connecting-to-github-with-ssh
  • gh CLI (gh repo create): https://cli.github.com/manual/gh_repo_create

Configurar VSCode con GitHub Copilot: Guía práctica

Resumen

Guía práctica para configurar GitHub Copilot en VSCode: custom instructions, prompt files, generación automática de conventional commits y uso de modos Ask/Edit/Agent para maximizar productividad en proyectos Azure/DevOps.

¿Qué es GitHub Copilot?

GitHub Copilot es un asistente de código basado en IA que sugiere código, genera tests, documenta funciones y responde preguntas técnicas directamente en tu editor. En VSCode, Copilot ofrece:

  • Completado de código inline: Sugerencias mientras escribes
  • Chat interactivo: Conversaciones sobre tu código
  • Generación de commits: Mensajes siguiendo conventional commits
  • Modos de trabajo: Ask, Edit y Agent para diferentes escenarios

Instalación en VSCode

Requisitos previos:

  • VSCode 1.99 o superior
  • Cuenta GitHub con acceso a Copilot (Copilot Free disponible)

Pasos de instalación:

  1. Abrir VSCode
  2. Ir a Extensions (Ctrl+Shift+X)
  3. Buscar "GitHub Copilot"
  4. Instalar dos extensiones:
  5. GitHub Copilot (autocompletado)
  6. GitHub Copilot Chat (chat interactivo)

Autenticación:

# VSCode solicitará autenticación con GitHub
# Alternativamente desde Command Palette (Ctrl+Shift+P):
GitHub Copilot: Sign In

Custom Instructions

Las instrucciones personalizadas permiten definir reglas y contexto que Copilot aplicará automáticamente, eliminando la necesidad de repetir el mismo contexto en cada prompt.

Tipos de archivos de instrucciones

VSCode soporta tres tipos de archivos Markdown para instrucciones:

1. .github/copilot-instructions.md (instrucciones globales del workspace)

  • Se aplica automáticamente a todas las conversaciones
  • Un único archivo en la raíz del repositorio
  • Compartido con todo el equipo vía Git

2. .instructions.md (instrucciones específicas por archivo/tarea)

  • Múltiples archivos para diferentes contextos
  • Usa frontmatter applyTo con glob patterns
  • Puede ser workspace o user-level (sincronizable)

3. AGENTS.md (experimental, para múltiples agentes IA)

  • En la raíz o subfolders del workspace
  • Útil si trabajas con varios agentes IA
  • Requiere habilitar chat.useAgentsMdFile

Estructura del proyecto:

your-repo/
├── .github/
│   ├── copilot-instructions.md    ← Global workspace
│   ├── instructions/               ← Instrucciones específicas
│   │   ├── python.instructions.md
│   │   └── terraform.instructions.md
│   └── prompts/                    ← Prompts reutilizables
├── AGENTS.md                        ← Para múltiples agentes (experimental)
├── src/
└── README.md

Ejemplo de instrucciones para proyectos Azure/DevOps:

# Instrucciones para GitHub Copilot

## Contexto del proyecto

Este repositorio contiene infraestructura Azure gestionada con Terraform y CI/CD con GitHub Actions.

## Convenciones de código

### Terraform

- Usar módulos para componentes reutilizables
- Variables en `variables.tf`, outputs en `outputs.tf`
- Naming: `<recurso>-<entorno>-<región>` (ejemplo: `st-prod-weu`)
- Tags obligatorios: Environment, Owner, CostCenter

### Python

- PEP 8 para estilo de código
- Type hints obligatorios en funciones públicas
- Docstrings en formato Google
- Tests con pytest, coverage mínimo 80%

### Commits

- Seguir conventional commits: `type(scope): description`
- Tipos permitidos: feat, fix, docs, chore, refactor, test
- Scope debe indicar área afectada: terraform, github-actions, scripts

## Seguridad

- NUNCA incluir credenciales hardcoded
- Usar Azure Key Vault para secretos
- Managed Identities sobre service principals
- Mínimo privilegio en roles RBAC

## Referencias

- Validar recursos Azure contra docs oficiales
- Seguir Azure Well-Architected Framework
- Bicep/Terraform: sintaxis actualizada

Habilitar Custom Instructions en VSCode

Opción 1: Habilitar en Settings

{
  "github.copilot.chat.codeGeneration.useInstructionFiles": true
}

Opción 2: Generar automáticamente desde tu workspace

VSCode puede analizar tu código y generar instrucciones que coincidan con tus prácticas:

  1. Chat view → Configure ChatGenerate Instructions
  2. Revisar y editar el archivo generado
  3. Guardar en .github/copilot-instructions.md

Validación: Cuando Copilot use las instrucciones, aparecerá el archivo en la lista de "References" de la respuesta.

Instrucciones específicas con .instructions.md

Además del archivo global, puedes crear instrucciones específicas para lenguajes, frameworks o tareas.

Formato de .instructions.md

Estructura:

---
description: "Descripción mostrada al hacer hover"
applyTo: "**/*.py"  # Glob pattern (relativo al workspace root)
---

# Instrucciones específicas para Python

- Seguir PEP 8
- Type hints obligatorios
- Docstrings en formato Google

Ejemplo: Instrucciones para Terraform

Archivo: .github/instructions/terraform.instructions.md

---
description: "Terraform coding standards"
applyTo: "**/*.tf,**/*.tfvars"
---

# Terraform Best Practices

- Usar módulos para componentes reutilizables
- Variables en variables.tf, outputs en outputs.tf
- Naming convention: `<resource>-<environment>-<region>`
- Tags obligatorios: Environment, Owner, CostCenter
- Encryption habilitado por defecto
- Validar con `terraform validate` y `tflint`

Crear instrucciones desde VSCode

Comando: Chat: New Instructions File (Ctrl+Shift+P)

  1. Elegir ubicación:
  2. Workspace: .github/instructions/ (compartido vía Git)
  3. User: Perfil de usuario (sincronizable entre dispositivos)
  4. Nombrar archivo (ej: python.instructions.md)
  5. Definir applyTo pattern en frontmatter
  6. Escribir instrucciones en Markdown

Adjuntar manualmente: En Chat view → botón +Instructions → seleccionar archivo

Sincronizar instrucciones de usuario

Las instrucciones user-level pueden sincronizarse entre dispositivos:

  1. Habilitar Settings Sync
  2. Ctrl+Shift+PSettings Sync: Configure
  3. Seleccionar Prompts and Instructions

Prompt Files (.github/prompts)

Los prompt files permiten crear plantillas de prompts reutilizables compartibles con el equipo.

Crear prompts reutilizables

Ejemplo: Prompt para generar Terraform modules

Archivo: .github/prompts/terraform-module.prompt.md

Crea un módulo Terraform para #selection siguiendo estas reglas:

1. Estructura estándar:
   - `main.tf`: recursos principales
   - `variables.tf`: inputs con validation
   - `outputs.tf`: valores exportados
   - `versions.tf`: provider constraints
   - `README.md`: documentación

2. Convenciones:
   - Variables: descripción, tipo, validación con condition
   - Outputs: descripción clara del valor exportado
   - Tags: usar merge() con var.tags

3. Seguridad:
   - Encryption habilitado por defecto
   - HTTPS obligatorio para endpoints
   - Logging habilitado

4. Documentación:
   - README con ejemplos de uso
   - Terraform-docs compatible

Archivo: .github/prompts/fix-security.prompt.md

Analiza #file en busca de problemas de seguridad:

- Credenciales hardcoded
- Endpoints HTTP (deben ser HTTPS)
- Secretos en logs
- Permisos excesivos en RBAC
- Resources sin encryption
- Network security groups demasiado permisivos

Proporciona fix aplicando principio de mínimo privilegio y Zero Trust.

Usar Prompt Files

En Copilot Chat:

# Referenciar prompt file
#prompt:terraform-module

# Combinar con referencias
#prompt:fix-security #file:main.tf

# Agregar contexto adicional
#prompt:terraform-module para Azure Storage con lifecycle policies

Desde UI:

  1. Abrir Copilot Chat (Ctrl+Alt+I)
  2. Click en botón +
  3. Seleccionar "Prompt Files"
  4. Elegir prompt deseado

Conventional Commits con Copilot

GitHub Copilot puede generar mensajes de commit siguiendo conventional commits automáticamente.

Configuración para Conventional Commits

Opción 1: Settings específico para commits (recomendado)

Desde VS Code 1.102+, usar setting dedicado:

{
  "github.copilot.chat.commitMessageGeneration.instructions": [
    {
      "text": "Seguir Conventional Commits 1.0.0: <type>(<scope>): <description>"
    },
    {
      "file": ".github/instructions/commit-standards.md"
    }
  ]
}

Opción 2: Agregar a .github/copilot-instructions.md

## Git Commits

Todos los commits deben seguir Conventional Commits 1.0.0:

**Formato**: `<type>(<scope>): <description>`

**Types permitidos**:

- `feat`: Nueva funcionalidad
- `fix`: Corrección de bug
- `docs`: Cambios en documentación
- `chore`: Tareas de mantenimiento (deps, config)
- `refactor`: Refactorización sin cambio funcional
- `test`: Añadir o modificar tests
- `ci`: Cambios en CI/CD
- `perf`: Mejoras de performance

**Scope**: Área del proyecto afectada (terraform, github-actions, scripts, docs)

**Ejemplos**:

```text
feat(terraform): add Azure Front Door module
fix(github-actions): correct OIDC authentication
docs(readme): update deployment instructions
chore(deps): bump terraform-azurerm to 4.0

Breaking changes: Usar ! o footer BREAKING CHANGE:

feat(terraform)!: migrate to azurerm 4.0

BREAKING CHANGE: provider azurerm 3.x no longer supported

Generar commits con Copilot

En Source Control panel de VSCode:

  1. Hacer cambios en archivos
  2. Stage changes
  3. Click en icono ✨ (Sparkle) en message box
  4. Copilot genera mensaje siguiendo conventional commits

Desde terminal con Git:

# Copilot CLI (requiere instalación adicional)
gh copilot suggest "git commit message for staged changes"

Ejemplo de mensaje generado:

feat(terraform): add network security group for AKS

- Create NSG with default deny rules
- Allow only required ports (443, 80)
- Associate to AKS subnet
- Add diagnostic settings for logging

Modos de Copilot Chat

Copilot ofrece tres modos de trabajo según el tipo de tarea.

Ask Mode (Modo pregunta)

Cuándo usar: Consultas, explicaciones, búsqueda de información.

Características:

  • Responde en el panel de chat
  • No modifica archivos
  • Proporciona ejemplos de código copiables

Ejemplos:

# Explicar código
Explica qué hace #selection

# Mejores prácticas
¿Cuáles son las mejores prácticas para Azure Storage lifecycle policies?

# Comparar opciones
Diferencia entre Azure Container Apps y AKS

# Troubleshooting
¿Por qué falla este módulo de Terraform? #file:main.tf

Edit Mode (Modo edición)

Cuándo usar: Modificar código existente, refactorizar.

Características:

  • Propone cambios con preview diff
  • Puedes aceptar/rechazar modificaciones
  • Trabaja en archivos abiertos

Activar Edit Mode:

  1. Seleccionar código
  2. Ctrl+I (inline chat)
  3. Escribir instrucción

Ejemplos:

# Refactor
Refactoriza #selection para usar módulos reutilizables

# Optimizar
Optimiza este loop para mejor performance

# Agregar tests
Genera unit tests para #selection usando pytest

# Documentar
Agrega docstrings a todas las funciones en #file

Agent Mode (Modo agente)

Cuándo usar: Tareas complejas multi-archivo, workflows completos.

Características:

  • Ejecuta acciones en todo el workspace
  • Crea/edita múltiples archivos
  • Ejecuta comandos en terminal
  • Requiere confirmación en pasos críticos

Activar Agent Mode:

  1. Abrir Copilot Chat
  2. Selector de modo → Agent
  3. Confirmar modo activo

Habilitar Agent Mode:

VSCode Settings (Ctrl+,):

{
  "chat.agent.enabled": true,
  "chat.agent.maxRequests": 128
}

Ejemplos de tareas con Agent Mode:

# Crear proyecto completo
Crea un proyecto Terraform para desplegar Azure Container Apps con:
- Virtual Network con subnets
- Azure Container Registry
- Log Analytics Workspace
- Application Insights
- GitHub Actions workflow para CI/CD

# Migrar código
Migra todos los recursos en #folder de azurerm 3.x a 4.x

# Generar documentación
Genera README.md completo para este repositorio incluyendo:
- Descripción
- Arquitectura (diagrama Mermaid)
- Prerequisites
- Deployment steps
- Troubleshooting

# Implementar tests
Crea suite completa de tests para todos los módulos Terraform

Best Practices para Agent Mode:

  1. Prompts granulares: Dividir tareas grandes en pasos pequeños
  2. Permitir que Copilot trabaje: Dejar que ejecute tareas en vez de hacerlas manualmente
  3. Revisar cambios: Validar modificaciones antes de commit
  4. Configurar auto-approve (opcional):
{
  "chat.tools.autoApprove": true
}

Slash Commands

Comandos rápidos para tareas comunes sin escribir prompts largos.

Command Descripción Ejemplo
/doc Generar documentación Seleccionar función → /doc
/explain Explicar código Seleccionar bloque → /explain
/fix Proponer correcciones Seleccionar error → /fix
/tests Generar unit tests Seleccionar función → /tests using pytest
/optimize Optimizar performance Seleccionar loop → /optimize
/generate Generar código nuevo /generate Azure Bicep for Storage Account

Uso combinado con contexto:

# Con archivos
/explain #file:main.tf

# Con selección
/tests #selection using XUnit

# Con scope
/fix the authentication logic in #file:auth.py

Herramientas de Agent Mode para Azure

Cuando trabajas con recursos Azure, Agent Mode tiene herramientas específicas.

Verificar herramientas disponibles:

What are your tools?

Herramientas Azure:

  • Azure CLI tools: Generar comandos az
  • Terraform tools: Crear/validar configuraciones
  • GitHub Actions tools: Generar workflows

Ejemplo práctico:

# Generar comando Azure CLI
¿Cuál es el comando az para listar todas mis storage accounts ordenadas por región?

Copilot genera:

az storage account list --query "sort_by([], &location)[].{Name:name, Location:location}" --output table

Mejores prácticas

Contexto efectivo

Proporcionar referencias específicas:

❌ MALO:
"Explica este código"

✅ BUENO:
"Explica #selection enfocándote en el flujo de autenticación OIDC"

Usar scope adecuado:

# Archivo completo
#file:main.tf

# Función específica
#selection

# Múltiples archivos
#file:variables.tf #file:outputs.tf

# Workspace
#codebase (usa con precaución, puede ser lento)

Organización de instrucciones

Por archivo de instrucciones:

  • Global (.github/copilot-instructions.md): Principios generales del proyecto
  • Por lenguaje (.github/instructions/python.instructions.md): Standards específicos
  • Por framework (.github/instructions/terraform.instructions.md): Convenciones del stack
  • Por tarea (.github/instructions/security-review.instructions.md): Workflows específicos

Usar applyTo patterns efectivos:

---
applyTo: "**/*.{ts,tsx}"  # TypeScript y React
---

---
applyTo: "src/backend/**"  # Backend folder
---

---
applyTo: "terraform/**/*.tf"  # Solo Terraform files
---

Seguridad

Validar código generado:

  • No confiar ciegamente en sugerencias
  • Revisar permisos y roles RBAC
  • Verificar que no expone credenciales
  • Comprobar configuraciones de red

Ejemplo de validación:

Analiza #file:main.tf y verifica:
1. ¿Hay credenciales hardcoded?
2. ¿Todos los recursos tienen encryption habilitado?
3. ¿Network security groups siguen principio de mínimo privilegio?
4. ¿Están definidos todos los tags obligatorios?

Iteración incremental

Trabajar por pasos:

# ❌ Prompt demasiado amplio:
"Crea infraestructura completa para microservicios en Azure"

# ✅ Iteración incremental:
# Paso 1:
"Crea networking base: VNET con 3 subnets (app, data, management)"

# Paso 2:
"Agrega Azure Container Apps environment con internal VNET"

# Paso 3:
"Configura Application Gateway con WAF"

Validación contra docs oficiales

Copilot puede estar desactualizado. Validar contra documentación oficial:

# Verificar sintaxis actualizada
Muestra la sintaxis actual de azurerm_storage_account en Terraform 1.9

# Contrastar con docs
¿Esta configuración sigue las mejores prácticas de Azure Well-Architected Framework para seguridad?

Tips oficiales (VSCode)

Según documentación oficial:

  1. Instrucciones cortas y autocontenidas: Una declaración por instrucción
  2. Múltiples archivos por tema: Usar .instructions.md con applyTo selectivo
  3. Workspace sobre user: Compartir con equipo vía Git
  4. Referenciar en prompts: Evitar duplicación con Markdown links
  5. Markdown links para contexto: [archivo](../path/file.ts) o URLs externas

Settings especializados

VSCode permite configurar instrucciones específicas para escenarios concretos:

Settings disponibles

Setting Uso
github.copilot.chat.commitMessageGeneration.instructions Generación de commit messages
github.copilot.chat.pullRequestDescriptionGeneration.instructions Descripción de PRs
github.copilot.chat.reviewSelection.instructions Code review
github.copilot.chat.codeGeneration.instructions Generación de código (deprecated)*
github.copilot.chat.testGeneration.instructions Generación de tests (deprecated)*

*Desde VS Code 1.102, usar .instructions.md files en su lugar.

Ejemplo completo en settings.json:

{
  // Commits con Conventional Commits
  "github.copilot.chat.commitMessageGeneration.instructions": [
    { "text": "Usar formato: <type>(<scope>): <description>" },
    { "text": "Types: feat, fix, docs, chore, refactor, test, ci, perf" }
  ],

  // PRs con checklist
  "github.copilot.chat.pullRequestDescriptionGeneration.instructions": [
    { "text": "Incluir siempre lista de cambios principales" },
    { "text": "Agregar sección Testing con casos probados" },
    { "file": ".github/instructions/pr-template.md" }
  ],

  // Code review enfocado en seguridad
  "github.copilot.chat.reviewSelection.instructions": [
    { "file": ".github/instructions/security-review.md" }
  ]
}

Configuración avanzada VSCode

Settings recomendados para Copilot (settings.json):

{
  // GitHub Copilot
  "github.copilot.enable": {
    "*": true,
    "yaml": true,
    "markdown": true,
    "terraform": true
  },

  // Instructions files
  "github.copilot.chat.codeGeneration.useInstructionFiles": true,
  "chat.instructionsFilesLocations": [
    ".github/instructions"  // Carpeta adicional para .instructions.md
  ],

  // AGENTS.md (experimental)
  "chat.useAgentsMdFile": false,  // true para habilitar
  "chat.useNestedAgentsMdFiles": false,  // subfolder support

  // Agent mode
  "chat.agent.enabled": true,
  "chat.agent.maxRequests": 128,

  // Editor
  "editor.inlineSuggest.enabled": true,
  "editor.suggestSelection": "first",

  // Opcional: Auto-approve (usar con precaución)
  "chat.tools.autoApprove": false
}

Keybindings personalizados (.vscode/keybindings.json):

[
  {
    "key": "ctrl+shift+i",
    "command": "workbench.action.chat.open"
  },
  {
    "key": "ctrl+i",
    "command": "inlineChat.start",
    "when": "editorFocus"
  }
]

Workflow recomendado

Flujo de trabajo típico con Copilot:

  1. Planificación:
# Agent Mode
Analiza los requisitos y propón arquitectura para [proyecto]
Genera diagrama Mermaid de la solución
  1. Implementación:
# Edit Mode
Usando #prompt:terraform-module, crea módulo para Azure Front Door
  1. Testing:
# Ask Mode
/tests #file:main.tf usando Terratest
  1. Documentación:
# Agent Mode
Genera documentación completa para #folder incluyendo:
- README con ejemplos
- Diagramas de arquitectura
- Runbooks de operaciones
  1. Commit:
# Source Control panel
Click en ✨ → genera conventional commit
  1. Validación:
# Ask Mode
Revisa #file:main.tf contra Azure security baseline

Troubleshooting

Copilot no responde

Verificar:

# Estado de extensión
Ctrl+Shift+P  "GitHub Copilot: Check Status"

# Logs
Ctrl+Shift+P  "GitHub Copilot: Open Logs"

Soluciones comunes:

  • Verificar autenticación GitHub
  • Reiniciar VSCode
  • Comprobar firewall/proxy (requiere acceso a *.github.com)

Sugerencias irrelevantes

Mejorar contexto:

  • Usar custom instructions más específicas
  • Proporcionar ejemplos en instructions
  • Referenciar archivos relacionados en prompt

Ejemplo:

❌ "Crea módulo Terraform"

✅ "Usando #file:examples/storage.tf como referencia, crea módulo 
   para #selection siguiendo #prompt:terraform-module"

Agent Mode pide demasiadas confirmaciones

Configurar auto-approve:

{
  "chat.tools.autoApprove": true
}

Alternativa: Click en "Always Allow" en diálogo de confirmación.

MCP Server de Awesome Copilot

El repositorio Awesome Copilot incluye un MCP Server que permite buscar e instalar instrucciones, prompts y chat modes directamente desde VSCode.

Instalación del MCP Server

Requiere Docker instalado y ejecutándose.

Instalar en VSCode:

  1. Install in VS Code
  2. Docker descargará la imagen automáticamente
  3. Usar comando en Chat: /awesome-copilot <query>

Ejemplo de uso:

/awesome-copilot create-readme
/awesome-copilot terraform best practices
/awesome-copilot security review

Recursos del repositorio Awesome Copilot

El repositorio contiene cientos de contribuciones de la comunidad:

  • Agents: Agentes especializados (Terraform, Security, Database, etc.)
  • Instructions: Standards de código por lenguaje/framework
  • Prompts: Tareas específicas (documentación, refactoring, testing)
  • Chat Modes: Personas IA (arquitecto, DBA, security expert)
  • Collections: Conjuntos curados por tema/workflow

Explorar:

Referencias

Git Workflows: GitFlow vs GitHub Flow vs Trunk-Based vs Release Flow

Resumen

Existen múltiples estrategias de branching Git para gestionar el desarrollo de software: GitFlow (modelo complejo con develop/feature/release/hotfix branches para equipos grandes), GitHub Flow (minimalista con feature branches + main + deploy before merge), GitLab Flow (híbrido con environment branches staging/production para control deployment), Trunk-Based Development (commits directos a main con short-lived branches <24h, usado por Google/Facebook), Release Flow (trunk-based + release branches sin merge back, usado por Microsoft Azure), OneFlow (GitFlow simplificado eliminando develop branch), y Feature Branch Workflow (básico con feature branches + pull requests). Cada estrategia se adapta a diferentes tamaños de equipo, frecuencia de releases, madurez CI/CD y control de environments.

Terraform: Uso de Variables y Secrets de GitHub

Resumen

Cómo usar variables y secrets de GitHub en despliegues con Terraform para Azure. Ejemplo práctico y buenas prácticas para admins y DevOps que quieren CI/CD seguro y reproducible.

¿Qué problema resuelve?

Permite gestionar credenciales y parámetros sensibles (como client secrets, IDs, claves) de forma segura en pipelines de GitHub Actions, evitando exponerlos en el código fuente.

¿Qué es y cómo funciona?

  • Secrets: Valores cifrados almacenados en GitHub (por repo o por entorno). Ej: credenciales Azure, claves API.
  • Variables de configuración: Valores no sensibles, útiles para parámetros de entorno, nombres, ubicaciones, flags, etc. Se gestionan en Settings → Secrets and variables → Actions → Variables.
  • Los workflows de GitHub Actions pueden acceder a estos valores como variables de entorno.
  • Terraform puede consumirlos usando la sintaxis ${{ secrets.SECRET_NAME }} para secrets y ${{ vars.VAR_NAME }} para variables de configuración en el workflow.

Ejemplo sencillo: despliegue de Azure Resource Group con variables y secrets

1. Añadir secrets en GitHub

  • Ve a tu repo → Settings → Secrets and variables → Actions
  • Añade los secrets:
  • AZURE_CLIENT_ID
  • AZURE_TENANT_ID
  • AZURE_SUBSCRIPTION_ID

2. Añadir variables de configuración (no sensibles)

  • Ejemplo: RG_NAME, LOCATION

3. Workflow de GitHub Actions (.github/workflows/terraform.yml)

name: 'Terraform Azure'
on:
  push:
    branches: [ main ]

---
  deploy:
    runs-on: ubuntu-latest
    env:
      # Recomendado: Federated Identity (OIDC) - no uses client secret
      ARM_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
      ARM_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
      ARM_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
      TF_VAR_rg_name: ${{ vars.RG_NAME }}
      TF_VAR_location: ${{ vars.LOCATION }}
    steps:
      - uses: actions/checkout@v4
      - name: Setup Terraform
        uses: hashicorp/setup-terraform@v3
      - name: Terraform Init
        run: terraform init
      - name: Terraform Plan
        run: terraform plan
      - name: Terraform Apply
        run: terraform apply -auto-approve

En este ejemplo, RG_NAME y LOCATION son variables de configuración definidas en GitHub (no secrets) y se pasan automáticamente como variables de entrada a Terraform usando el prefijo TF_VAR_.

4. Código Terraform (main.tf)

provider "azurerm" {
  features {}
}

variable "rg_name" {
  description = "Nombre del resource group"
  type        = string
}

variable "location" {
  description = "Ubicación Azure"
  type        = string
}

resource "azurerm_resource_group" "ejemplo" {
  name     = var.rg_name
  location = var.location
}

Objetos complejos y versiones recientes de Terraform

Desde Terraform 1.3 en adelante, puedes pasar mapas y listas directamente como variables de entorno usando el prefijo TF_VAR_, sin necesidad de jsonencode/jsondecode. Terraform los interpreta automáticamente si el valor es un JSON válido.

Ejemplo directo (Terraform >= 1.3)

env:
  TF_VAR_tags: '{"env":"prod","owner":"devops"}'
variable "tags" {
  type = map(string)
}

resource "azurerm_resource_group" "ejemplo" {
  name     = var.rg_name
  location = var.location
  tags     = var.tags
}
No necesitas usar jsondecode() si la variable ya es del tipo adecuado y el valor es JSON válido.

¿Y si necesitas un objeto complejo y usas versiones antiguas?

Si necesitas pasar un objeto complejo (por ejemplo, un mapa o lista de objetos) y tu versión de Terraform es anterior a 1.3, lo más habitual es usar un secret o variable en formato JSON y parsearlo en Terraform.

env:
  TF_VAR_tags_json: ${{ secrets.TAGS_JSON }}
variable "tags_json" {
  description = "Tags en formato JSON"
  type        = string
}

locals {
  tags = jsondecode(var.tags_json)
}

resource "azurerm_resource_group" "ejemplo" {
  name     = var.rg_name
  location = var.location
  tags     = local.tags
}
Así puedes pasar cualquier estructura compleja (mapas, listas, objetos anidados) como string JSON y usar jsondecode() en Terraform para convertirlo a un tipo nativo. draft: false date: 2025-10-23 authors:

  • rfernandezdo categories:

  • DevOps

  • Terraform
  • GitHub Actions tags:

  • Terraform

  • GitHub Secrets
  • Azure

Buenas prácticas

  • Usa federated identity (OIDC) siempre que sea posible, evita client secrets.
  • Nunca subas secrets al repo ni los hardcodees en el código.
  • Usa GitHub Environments para separar prod/dev y limitar acceso a secrets.
  • Usa variables para parámetros no sensibles (ej: nombres, ubicaciones).
  • Valida siempre con terraform validate antes de aplicar.
  • Rota los secrets periódicamente si usas client secrets legacy.

Referencias

Cómo Resolver Divergencias en Git: Sincronizando Ramas Local y Remota

El Problema

Al intentar hacer git pull, te encuentras con el siguiente error:

hint: You have divergent branches and need to specify how to reconcile them.
hint: You can do so by running one of the following commands sometime before
hint: your next pull:
hint:
hint:   git config pull.rebase false  # merge
hint:   git config pull.rebase true   # rebase
hint:   git config pull.ff only       # fast-forward only

Este mensaje indica que tu rama local y la rama remota han divergido: ambas tienen commits únicos que la otra no tiene. Git necesita que decidas cómo combinar estos cambios.

¿Por Qué Ocurre?

La divergencia sucede cuando:

  1. Trabajas localmente y realizas commits en tu rama main
  2. Mientras tanto, cambios se fusionan en el remoto (por ejemplo, a través de Pull Requests)
  3. Ambas ramas tienen commits exclusivos que la otra no posee

Ejemplo visual:

Local:   A---B---C---L1
                      ^(tu commit local)
Remote:  A---B---C---R1---R2---R3
                      ^(commits del remoto vía PR)

Análisis: Identificar la Divergencia

Antes de resolver, es crucial entender qué ha divergido.

Paso 1: Sincronizar con el Remoto

git fetch origin main --tags

Este comando descarga los cambios del remoto sin fusionarlos.

Paso 2: Comparar Commits

Verifica qué commits están únicamente en cada lado:

# Commits que tienes localmente pero no están en el remoto
git log origin/main..main --oneline

# Commits que están en el remoto pero no tienes localmente
git log main..origin/main --oneline

Paso 3: Revisar los HEADs

# Ver el commit actual local
git rev-parse HEAD

# Ver el commit actual remoto
git rev-parse origin/main

Ejemplo Real

En nuestro caso, obtuvimos:

--- Commits en local, no en remoto ---
81e3b93 feat: add new feature for data processing

--- Commits en remoto, no en local ---
9d3e3e9 Merge pull request #42 from team/feature/update-docs
285a538 Complete documentation review - all documents validated
000b9cb Fix configuration and add new parameters
a7694d3 Initial implementation plan

Interpretación: El remoto tiene un PR fusionado con 4 commits que no tenemos localmente, y nosotros tenemos 1 commit local que el remoto no tiene.

Estrategias de Resolución

Existen tres estrategias principales:

1. Merge (Recomendado para Trabajo Colaborativo)

Cuándo usarlo: Cuando trabajas con un equipo y quieres preservar toda la historia, incluyendo los merge commits de PRs.

Ventajas:

  • ✅ Preserva la historia completa (incluidos merge commits)
  • ✅ Muestra claramente cuándo se integraron features
  • ✅ Más seguro: no reescribe historia

Desventajas:

  • ⚠️ Crea un commit de merge adicional
  • ⚠️ Historia no completamente lineal

Comando:

git merge origin/main --no-edit

Si prefieres revisar/editar el mensaje de merge:

git merge origin/main

2. Rebase (Para Historia Lineal)

Cuándo usarlo: Cuando trabajas solo o en una feature branch que aún no has compartido públicamente.

Ventajas:

  • ✅ Historia completamente lineal
  • ✅ Más limpia para revisar con git log

Desventajas:

  • ⚠️ Reescribe commits locales (cambian sus SHAs)
  • ⚠️ Puede causar problemas si ya compartiste estos commits
  • ⚠️ Pierdes el contexto de cuándo se integró el PR remoto

Comando:

git rebase origin/main

3. Fast-Forward Only (Restrictivo)

Cuándo usarlo: Cuando quieres asegurarte de que solo hagas pull si no hay divergencia.

Comando:

git config pull.ff only
git pull

Si hay divergencia, fallará y tendrás que decidir merge o rebase manualmente.

Solución Paso a Paso (Merge)

1. Analizar la Situación

# Sincronizar con remoto
git fetch origin main --tags

# Ver divergencias
echo "--- Commits locales únicos ---"
git log origin/main..main --oneline

echo "--- Commits remotos únicos ---"
git log main..origin/main --oneline

2. Decidir Estrategia

Para equipos colaborativos con PRs, merge es la opción más segura:

git merge origin/main --no-edit

3. Resolver Conflictos (Si Ocurren)

Si hay conflictos, Git te lo indicará:

Auto-merging some-file.md
CONFLICT (content): Merge conflict in some-file.md
Automatic merge failed; fix conflicts and then commit the result.

Pasos para resolver:

a) Abre el archivo con conflictos:

<<<<<<< HEAD
Tu versión local
=======
Versión del remoto
>>>>>>> origin/main

b) Edita manualmente para quedarte con el contenido correcto

c) Marca como resuelto:

git add some-file.md

d) Completa el merge:

git commit -m "Merge origin/main into main"

4. Verificar el Resultado

# Ver el log reciente
git log --oneline --graph -10

Deberías ver algo como:

*   3e1ed57 (HEAD -> main) Merge remote-tracking branch 'origin/main' into main
|\
| * 9d3e3e9 (origin/main) Merge pull request #42
| * 285a538 Complete documentation review
| * 000b9cb Fix configuration parameters
| * a7694d3 Initial implementation plan
* | 81e3b93 feat: add new feature for data processing
|/
* d140dce feat: improved monthly workflow

5. Empujar los Cambios

git push origin main

Salida esperada:

Enumerating objects: 15, done.
Counting objects: 100% (12/12), done.
Delta compression using up to 8 threads
Compressing objects: 100% (7/7), done.
Writing objects: 100% (7/7), 1.08 KiB | 1.08 MiB/s, done.
Total 7 (delta 5), reused 0 (delta 0)
remote: Resolving deltas: 100% (5/5), completed with 4 local objects.
To https://github.com/tu-usuario/tu-repositorio.git
   9d3e3e9..3e1ed57  main -> main

Verificación Post-Merge

Comprobar Archivos Críticos

Si tienes workflows de CI/CD u otros archivos críticos, verifica su integridad:

# Buscar un patrón específico en un archivo
grep -n "specific_pattern" .github/workflows/deploy.yml

# O leer el archivo completo
cat .github/workflows/deploy.yml

Confirmar Sincronización

# Ambos deberían apuntar al mismo commit ahora
git rev-parse HEAD
git rev-parse origin/main

Configuración Permanente

Si quieres establecer una estrategia por defecto para futuros git pull:

Para Merge (Recomendado)

git config pull.rebase false

Para Rebase

git config pull.rebase true

Global (Todos tus Repos)

Añade --global:

git config --global pull.rebase false

Mejores Prácticas

✅ Hacer Fetch Frecuentemente

# Ejecutar cada mañana o antes de empezar a trabajar
git fetch origin

Esto te mantiene informado de cambios remotos sin afectar tu trabajo local.

✅ Trabajar en Feature Branches

En lugar de trabajar directamente en main:

git checkout -b feature/my-feature
# ... hacer commits ...
git push origin feature/my-feature
# Abrir PR en GitHub

Esto evita divergencias en main.

✅ Pull Antes de Push

git fetch origin
git status
# Si hay cambios remotos, decide merge o rebase
git pull
# Ahora puedes pushear
git push

⚠️ Evitar Force Push en Ramas Compartidas

# ❌ NUNCA en main compartido
git push --force origin main

# ✅ OK solo en tus feature branches
git push --force origin feature/my-feature

Casos Especiales

Si Hiciste Rebase por Error y Quieres Revertir

# Ver el reflog (historial de cambios de HEAD)
git reflog

# Volver a un estado anterior
git reset --hard HEAD@{2}

Si Quieres Descartar Tus Commits Locales

# ⚠️ CUIDADO: Esto descarta tus commits locales permanentemente
git reset --hard origin/main

Si Quieres Preservar Cambios Locales Sin Commit

# Guardar cambios temporalmente
git stash

# Sincronizar con remoto
git pull

# Recuperar tus cambios
git stash pop

Resumen: Checklist Rápido

  • Fetch: git fetch origin main --tags
  • Analizar: git log origin/main..main --oneline y git log main..origin/main --oneline
  • Decidir: Merge (preservar historia) vs Rebase (lineal)
  • Ejecutar: git merge origin/main o git rebase origin/main
  • Resolver: Conflictos si los hay
  • Verificar: git log --graph --oneline -10
  • Push: git push origin main
  • Confirmar: Archivos críticos intactos

Conclusión

La divergencia de ramas es un escenario común en equipos colaborativos. La clave está en:

  1. Entender qué ha divergido (análisis con git log)
  2. Elegir la estrategia correcta (merge para equipos, rebase para trabajo local)
  3. Verificar el resultado antes de hacer push

En entornos colaborativos con Pull Requests, merge es generalmente la opción más segura ya que preserva la historia completa y evita reescribir commits que otros pueden estar usando como base.


Referencias

Azure Bicep avanzado: testing, linting y despliegue seguro multi-entorno

Resumen

Bicep permite IaC declarativo en Azure, pero en producción necesitas testing, linting y despliegue seguro por entorno. Este post va al grano: cómo testear plantillas, usar linting, parametrizar para dev/prod y evitar errores comunes.

¿Qué es Bicep avanzado?

  • Modularización y reutilización
  • Testing de plantillas antes de deploy
  • Linting para calidad y seguridad
  • Parametrización por entorno (dev, prod, test)
  • Integración con pipelines CI/CD

Arquitectura / Cómo funciona

flowchart LR
    Dev[Dev] --> CI[CI Pipeline]
    CI --> Lint[Lint]
    CI --> Test[Test]
    CI --> Deploy[Deploy]
    Deploy --> Azure[Azure]
    Azure --> RG1[Resource Group Dev]
    Azure --> RG2[Resource Group Prod]

Testing de plantillas Bicep

  1. Validar sintaxis:
    bicep build main.bicep
    
  2. Test de despliegue (dry-run):
    az deployment sub validate \
      --location westeurope \
      --template-file main.bicep \
      --parameters environment="dev"
    
  3. Test unitario con PSRule for Azure:
    pwsh -c "Invoke-PSRule -Path ./main.bicep"
    

Linting y calidad

  • Usar bicep linter:
    bicep build main.bicep
    bicep linter main.bicep
    
  • Reglas custom en .bicepconfig.json:
    {
      "analyzers": {
        "core": {
          "rules": {
            "no-hardcoded-secrets": "warning",
            "secure-parameters": "error"
          }
        }
      }
    }
    

Parametrización multi-entorno

  • Usar parámetros y archivos por entorno:
    az deployment group create \
      --resource-group rg-dev \
      --template-file main.bicep \
      --parameters @dev.parameters.json
    az deployment group create \
      --resource-group rg-prod \
      --template-file main.bicep \
      --parameters @prod.parameters.json
    
  • Ejemplo de parámetros:
    {
      "environment": {"value": "dev"},
      "adminPassword": {"value": "SuperSecret123!"}
    }
    

Integración CI/CD

  • Pipeline YAML ejemplo:
    trigger:
      - main
    pool:
      vmImage: 'ubuntu-latest'
    steps:
      - script: bicep build main.bicep
        displayName: 'Build Bicep'
      - script: bicep linter main.bicep
        displayName: 'Lint Bicep'
      - script: pwsh -c "Invoke-PSRule -Path ./main.bicep"
        displayName: 'Test Bicep'
      - script: az deployment group create --resource-group rg-dev --template-file main.bicep --parameters @dev.parameters.json
        displayName: 'Deploy Dev'
    

Buenas prácticas

  • Nunca hardcodear secretos en plantillas
  • Usar Key Vault para parámetros sensibles
  • Validar y testear antes de cada despliegue
  • Mantener módulos reutilizables y versionados
  • Revisar resultados de linting y testing en cada PR

Costes

  • Bicep: gratis
  • PSRule: gratis
  • Azure DevOps: desde $0 (hasta 1,800 min/mes)

Referencias

Azure DevOps: YAML templates reutilizables

Resumen

Templates eliminan código duplicado en pipelines. template parameters, extends y multistage.

Introducción

[Contenido técnico detallado a desarrollar]

Configuración básica

# Comandos de ejemplo
RG="my-rg"
LOCATION="westeurope"

# Comandos Azure CLI
az group create --name $RG --location $LOCATION

Casos de uso

  • Caso 1: [Descripción]
  • Caso 2: [Descripción]
  • Caso 3: [Descripción]

Buenas prácticas

  • Práctica 1: Descripción
  • Práctica 2: Descripción
  • Práctica 3: Descripción

Monitoreo y troubleshooting

# Comandos de diagnóstico
az monitor metrics list --resource ...

Referencias

GitHub Actions: Deploy a Azure con Workload Identity Federation

Resumen

Olv

ídate de guardar service principal secrets en GitHub. Con Workload Identity Federation, GitHub Actions se autentica a Azure sin credenciales almacenadas. Más seguro y cero rotación de secrets.

¿Qué es Workload Identity Federation?

Permite que GitHub Actions obtenga un access token de Azure usando OIDC (OpenID Connect). Azure confía en los tokens de GitHub basándose en: - Repositorio específico - Branch específico - Environment específico

Setup completo

1. Crear service principal

# Variables
APP_NAME="github-actions-federation"
RG="my-rg"
SUBSCRIPTION_ID=$(az account show --query id -o tsv)

# Crear app registration
APP_ID=$(az ad app create \
  --display-name $APP_NAME \
  --query appId -o tsv)

# Crear service principal
az ad sp create --id $APP_ID

SP_OBJECT_ID=$(az ad sp show --id $APP_ID --query id -o tsv)

# Asignar rol Contributor
az role assignment create \
  --role Contributor \
  --assignee $APP_ID \
  --scope /subscriptions/$SUBSCRIPTION_ID/resourceGroups/$RG

2. Configurar Federated Credential

# Para branch main
az ad app federated-credential create \
  --id $APP_ID \
  --parameters '{
    "name": "github-main-branch",
    "issuer": "https://token.actions.githubusercontent.com",
    "subject": "repo:myorg/myrepo:ref:refs/heads/main",
    "description": "GitHub Actions - main branch",
    "audiences": ["api://AzureADTokenExchange"]
  }'

# Para environment production
az ad app federated-credential create \
  --id $APP_ID \
  --parameters '{
    "name": "github-prod-environment",
    "issuer": "https://token.actions.githubusercontent.com",
    "subject": "repo:myorg/myrepo:environment:production",
    "description": "GitHub Actions - production environment",
    "audiences": ["api://AzureADTokenExchange"]
  }'

# Para pull requests
az ad app federated-credential create \
  --id $APP_ID \
  --parameters '{
    "name": "github-pull-requests",
    "issuer": "https://token.actions.githubusercontent.com",
    "subject": "repo:myorg/myrepo:pull_request",
    "description": "GitHub Actions - pull requests",
    "audiences": ["api://AzureADTokenExchange"]
  }'

3. Configurar GitHub Secrets

En tu repositorio → Settings → Secrets:

AZURE_CLIENT_ID = {APP_ID}
AZURE_TENANT_ID = {TENANT_ID}
AZURE_SUBSCRIPTION_ID = {SUBSCRIPTION_ID}

4. GitHub Actions Workflow

.github/workflows/deploy.yml:

name: Deploy to Azure

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

permissions:
  id-token: write  # Required for OIDC
  contents: read

jobs:
  deploy:
    runs-on: ubuntu-latest
    environment: production  # Si usas federated credential por environment

    steps:
      - uses: actions/checkout@v4

      - name: Azure Login with OIDC
        uses: azure/login@v1
        with:
          client-id: ${{ secrets.AZURE_CLIENT_ID }}
          tenant-id: ${{ secrets.AZURE_TENANT_ID }}
          subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}

      - name: Deploy to Azure Web App
        uses: azure/webapps-deploy@v2
        with:
          app-name: my-web-app
          package: ./dist

      - name: Azure CLI commands
        run: |
          az group list
          az webapp list --resource-group ${{ env.RG }}

Ventajas vs Service Principal Secrets

Aspecto Service Principal Secret Workload Identity
Secrets en GitHub Sí (password) No (solo IDs)
Rotación Manual cada 90 días Automática
Scope Amplio Granular (repo/branch/env)
Audit trail Limitado Completo (OIDC claims)
Expiración Hasta 2 años Token expira en minutos

Debugging

- name: Debug OIDC Token
  run: |
    curl -H "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" \
         "$ACTIONS_ID_TOKEN_REQUEST_URL&audience=api://AzureADTokenExchange" \
         | jq -R 'split(".") | .[1] | @base64d | fromjson'

Claims del token:

{
  "iss": "https://token.actions.githubusercontent.com",
  "sub": "repo:myorg/myrepo:ref:refs/heads/main",
  "aud": "api://AzureADTokenExchange",
  "ref": "refs/heads/main",
  "sha": "abc123...",
  "repository": "myorg/myrepo",
  "repository_owner": "myorg",
  "run_id": "123456789",
  "workflow": "Deploy to Azure"
}

Buenas prácticas

  • Usa environments: Requiere aprobaciones manuales para producción
  • Scope mínimo: Asigna roles solo al resource group necesario
  • Multiple credentials: Diferentes para dev/staging/prod
  • Branch protection: Solo permite deploy desde branches protegidos
  • Audit logs: Revisa sign-in logs en Azure AD regularmente

Referencias

Terraform Backend en Azure Storage: Setup completo

Resumen

Guardar el estado de Terraform en local es peligroso y no escala. Azure Storage con state locking es la solución estándar para equipos. Aquí el setup completo en 5 minutos.

¿Por qué usar remote backend?

Problemas con backend local: - ❌ Estado se pierde si borras tu laptop - ❌ Imposible colaborar en equipo - ❌ No hay locking → corrupciones en despliegues simultáneos - ❌ Secretos en plaintext en disco local

Ventajas con Azure Storage: - ✅ Estado centralizado y versionado - ✅ Locking automático con blob lease - ✅ Encriptación at-rest - ✅ Acceso controlado con RBAC

Setup del backend

1. Crear Storage Account

# Variables
RG="terraform-state-rg"
LOCATION="westeurope"
STORAGE_ACCOUNT="tfstate$(date +%s)"  # Nombre único
CONTAINER="tfstate"

# Crear resource group
az group create \
  --name $RG \
  --location $LOCATION

# Crear storage account
az storage account create \
  --name $STORAGE_ACCOUNT \
  --resource-group $RG \
  --location $LOCATION \
  --sku Standard_LRS \
  --encryption-services blob \
  --min-tls-version TLS1_2

# Crear container
az storage container create \
  --name $CONTAINER \
  --account-name $STORAGE_ACCOUNT \
  --auth-mode login

Naming convention

Storage account solo acepta lowercase y números, máximo 24 caracteres. Usa un sufijo único para evitar colisiones.

2. Configurar backend en Terraform

backend.tf:

terraform {
  backend "azurerm" {
    resource_group_name  = "terraform-state-rg"
    storage_account_name = "tfstate1234567890"
    container_name       = "tfstate"
    key                  = "prod.terraform.tfstate"
  }
}

3. Inicializar

# Login a Azure
az login

# Inicializar backend
terraform init

# Migrar estado existente (si aplica)
terraform init -migrate-state

State locking

Azure usa blob leases para locking automático:

# Ver si hay lock activo
az storage blob show \
  --container-name $CONTAINER \
  --name prod.terraform.tfstate \
  --account-name $STORAGE_ACCOUNT \
  --query "properties.lease.status"

Si alguien está ejecutando terraform apply, verás:

"locked"

Multi-entorno con workspaces

# Crear workspace por entorno
terraform workspace new dev
terraform workspace new staging  
terraform workspace new prod

# Listar workspaces
terraform workspace list

# Cambiar entre entornos
terraform workspace select prod

Cada workspace crea su propio state file:

prod.terraform.tfstate
dev.terraform.tfstate
staging.terraform.tfstate

Seguridad: Managed Identity

Evita usar access keys en pipelines:

# Crear managed identity
az identity create \
  --resource-group $RG \
  --name terraform-identity

# Asignar rol Storage Blob Data Contributor
IDENTITY_ID=$(az identity show \
  --resource-group $RG \
  --name terraform-identity \
  --query principalId -o tsv)

az role assignment create \
  --role "Storage Blob Data Contributor" \
  --assignee $IDENTITY_ID \
  --scope /subscriptions/{sub-id}/resourceGroups/$RG/providers/Microsoft.Storage/storageAccounts/$STORAGE_ACCOUNT

Backend config con managed identity:

terraform {
  backend "azurerm" {
    resource_group_name  = "terraform-state-rg"
    storage_account_name = "tfstate1234567890"
    container_name       = "tfstate"
    key                  = "prod.terraform.tfstate"
    use_msi              = true
    subscription_id      = "00000000-0000-0000-0000-000000000000"
    tenant_id            = "00000000-0000-0000-0000-000000000000"
  }
}

Versionado del estado

# Habilitar versioning en el container
az storage blob service-properties update \
  --account-name $STORAGE_ACCOUNT \
  --enable-versioning true

# Ver versiones anteriores
az storage blob list \
  --container-name $CONTAINER \
  --account-name $STORAGE_ACCOUNT \
  --include v \
  --query "[?name=='prod.terraform.tfstate'].{Version:versionId, LastModified:properties.lastModified}"

Pipeline CI/CD con Azure DevOps

azure-pipelines.yml:

trigger:
  - main

pool:
  vmImage: 'ubuntu-latest'

variables:
  - group: terraform-variables

stages:
  - stage: Plan
    jobs:
      - job: TerraformPlan
        steps:
          - task: AzureCLI@2
            displayName: 'Terraform Init & Plan'
            inputs:
              azureSubscription: 'Azure Service Connection'
              scriptType: 'bash'
              scriptLocation: 'inlineScript'
              inlineScript: |
                terraform init
                terraform plan -out=tfplan

          - publish: '$(System.DefaultWorkingDirectory)/tfplan'
            artifact: tfplan

  - stage: Apply
    dependsOn: Plan
    condition: succeeded()
    jobs:
      - deployment: TerraformApply
        environment: 'production'
        strategy:
          runOnce:
            deploy:
              steps:
                - download: current
                  artifact: tfplan

                - task: AzureCLI@2
                  displayName: 'Terraform Apply'
                  inputs:
                    azureSubscription: 'Azure Service Connection'
                    scriptType: 'bash'
                    scriptLocation: 'inlineScript'
                    inlineScript: |
                      terraform init
                      terraform apply tfplan

Troubleshooting

Error: "Failed to lock state"

# Forzar unlock (solo si estás seguro)
terraform force-unlock <LOCK_ID>

# O romper lease manualmente
az storage blob lease break \
  --container-name $CONTAINER \
  --blob-name prod.terraform.tfstate \
  --account-name $STORAGE_ACCOUNT

Error: "storage account not found"

# Verificar permisos
az storage account show \
  --name $STORAGE_ACCOUNT \
  --resource-group $RG

Buenas prácticas

  • State file por proyecto: No mezcles infraestructuras diferentes
  • Soft delete: Habilita soft delete en storage account
  • Network security: Restringe acceso desde VNet específicas
  • Audit logs: Habilita diagnostic settings para compliance
  • Backup externo: Exporta estados críticos a otro storage account

Never commit state files

Añade *.tfstate* a .gitignore. El state contiene secretos en plaintext.

Referencias