claude-agent-framework 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. package/README.md +128 -0
  2. package/bin/claude-framework +3 -0
  3. package/framework/agents/design-lead.md +240 -0
  4. package/framework/agents/product-owner.md +179 -0
  5. package/framework/agents/tech-lead.md +226 -0
  6. package/framework/commands/ayuda.md +127 -0
  7. package/framework/commands/a/303/261adir.md +98 -0
  8. package/framework/commands/backup.md +397 -0
  9. package/framework/commands/cambiar.md +110 -0
  10. package/framework/commands/cloud.md +457 -0
  11. package/framework/commands/code.md +142 -0
  12. package/framework/commands/debug.md +334 -0
  13. package/framework/commands/deploy.md +383 -0
  14. package/framework/commands/deshacer.md +120 -0
  15. package/framework/commands/estado.md +218 -0
  16. package/framework/commands/explica.md +227 -0
  17. package/framework/commands/feature.md +120 -0
  18. package/framework/commands/git.md +427 -0
  19. package/framework/commands/historial.md +202 -0
  20. package/framework/commands/learn.md +408 -0
  21. package/framework/commands/movil.md +245 -0
  22. package/framework/commands/nuevo.md +118 -0
  23. package/framework/commands/plan.md +134 -0
  24. package/framework/commands/prd.md +113 -0
  25. package/framework/commands/probar.md +148 -0
  26. package/framework/commands/revisar.md +208 -0
  27. package/framework/commands/seeds.md +230 -0
  28. package/framework/commands/seguridad.md +226 -0
  29. package/framework/commands/tasks.md +157 -0
  30. package/framework/skills/architecture/algorithms.md +970 -0
  31. package/framework/skills/architecture/clean-code.md +1080 -0
  32. package/framework/skills/architecture/design-patterns.md +1984 -0
  33. package/framework/skills/architecture/functional-programming.md +972 -0
  34. package/framework/skills/architecture/solid.md +991 -0
  35. package/framework/skills/cloud/cloud-aws.md +848 -0
  36. package/framework/skills/cloud/cloud-azure.md +931 -0
  37. package/framework/skills/cloud/cloud-gcp.md +848 -0
  38. package/framework/skills/cloud/message-queues.md +1229 -0
  39. package/framework/skills/core/accessibility.md +401 -0
  40. package/framework/skills/core/api.md +474 -0
  41. package/framework/skills/core/authentication.md +306 -0
  42. package/framework/skills/core/authorization.md +388 -0
  43. package/framework/skills/core/background-jobs.md +341 -0
  44. package/framework/skills/core/caching.md +473 -0
  45. package/framework/skills/core/code-review.md +341 -0
  46. package/framework/skills/core/controllers.md +290 -0
  47. package/framework/skills/core/cua.md +285 -0
  48. package/framework/skills/core/documentation.md +472 -0
  49. package/framework/skills/core/file-uploads.md +351 -0
  50. package/framework/skills/core/hotwire-native.md +296 -0
  51. package/framework/skills/core/hotwire.md +278 -0
  52. package/framework/skills/core/i18n.md +334 -0
  53. package/framework/skills/core/imports-exports.md +750 -0
  54. package/framework/skills/core/infrastructure.md +337 -0
  55. package/framework/skills/core/models.md +228 -0
  56. package/framework/skills/core/notifications.md +672 -0
  57. package/framework/skills/core/payments.md +581 -0
  58. package/framework/skills/core/performance.md +361 -0
  59. package/framework/skills/core/rails-scaffold.md +131 -0
  60. package/framework/skills/core/search.md +518 -0
  61. package/framework/skills/core/security.md +565 -0
  62. package/framework/skills/core/seeds.md +307 -0
  63. package/framework/skills/core/seo.md +542 -0
  64. package/framework/skills/core/testing.md +393 -0
  65. package/framework/skills/core/views.md +260 -0
  66. package/framework/skills/core/websockets.md +564 -0
  67. package/framework/skills/data/advanced-sql.md +1204 -0
  68. package/framework/skills/data/nosql.md +1141 -0
  69. package/framework/skills/devops/containers-advanced.md +1237 -0
  70. package/framework/skills/devops/debugging.md +834 -0
  71. package/framework/skills/devops/git-workflow.md +752 -0
  72. package/framework/skills/devops/networking.md +932 -0
  73. package/framework/skills/devops/shell-scripting.md +1132 -0
  74. package/framework/sub-agents/architecture-patterns-agent.md +1450 -0
  75. package/framework/sub-agents/cloud-agent.md +677 -0
  76. package/framework/sub-agents/data.md +504 -0
  77. package/framework/sub-agents/debugging-agent.md +554 -0
  78. package/framework/sub-agents/devops.md +483 -0
  79. package/framework/sub-agents/docs.md +176 -0
  80. package/framework/sub-agents/frontend-dev.md +349 -0
  81. package/framework/sub-agents/git-workflow-agent.md +697 -0
  82. package/framework/sub-agents/integrations.md +630 -0
  83. package/framework/sub-agents/native-dev.md +434 -0
  84. package/framework/sub-agents/qa.md +138 -0
  85. package/framework/sub-agents/rails-dev.md +375 -0
  86. package/framework/sub-agents/security.md +526 -0
  87. package/framework/sub-agents/ui.md +437 -0
  88. package/framework/sub-agents/ux.md +284 -0
  89. package/framework/templates/api-spec.md +500 -0
  90. package/framework/templates/component-spec.md +248 -0
  91. package/framework/templates/feature.json +13 -0
  92. package/framework/templates/model-spec.md +318 -0
  93. package/framework/templates/prd-template.md +80 -0
  94. package/framework/templates/task-plan.md +122 -0
  95. package/framework/templates/task-user-story.md +52 -0
  96. package/framework/templates/technical-spec.md +260 -0
  97. package/framework/templates/user-story.md +95 -0
  98. package/package.json +42 -0
  99. package/project-templates/CLAUDE.md +42 -0
  100. package/project-templates/contexts/architecture.md +25 -0
  101. package/project-templates/contexts/conventions.md +46 -0
  102. package/project-templates/contexts/design-system.md +47 -0
  103. package/project-templates/contexts/requirements.md +38 -0
  104. package/project-templates/contexts/stack.md +30 -0
  105. package/project-templates/history/active/models.md +11 -0
  106. package/project-templates/history/changelog.md +15 -0
  107. package/project-templates/workspace/.gitkeep +0 -0
  108. package/src/cli.js +52 -0
  109. package/src/init.js +104 -0
  110. package/src/status.js +75 -0
  111. package/src/update.js +88 -0
@@ -0,0 +1,931 @@
1
+ # Skill: Microsoft Azure para Rails
2
+
3
+ ## Purpose
4
+
5
+ Configurar, desplegar y gestionar aplicaciones Rails en Microsoft Azure, aprovechando los servicios cloud para escalabilidad, rendimiento y seguridad.
6
+
7
+ ## App Service
8
+
9
+ ### Configurar Web App para Rails
10
+
11
+ ```bash
12
+ # Crear grupo de recursos
13
+ az group create --name rails-app-rg --location eastus
14
+
15
+ # Crear App Service Plan
16
+ az appservice plan create \
17
+ --name rails-app-plan \
18
+ --resource-group rails-app-rg \
19
+ --sku B1 \
20
+ --is-linux
21
+
22
+ # Crear Web App con Ruby
23
+ az webapp create \
24
+ --resource-group rails-app-rg \
25
+ --plan rails-app-plan \
26
+ --name mi-rails-app \
27
+ --runtime "RUBY:3.1"
28
+
29
+ # Configurar variables de entorno
30
+ az webapp config appsettings set \
31
+ --resource-group rails-app-rg \
32
+ --name mi-rails-app \
33
+ --settings \
34
+ RAILS_ENV=production \
35
+ RAILS_SERVE_STATIC_FILES=true \
36
+ RAILS_LOG_TO_STDOUT=true
37
+ ```
38
+
39
+ ### Deployment desde GitHub
40
+
41
+ ```yaml
42
+ # .github/workflows/azure-deploy.yml
43
+ name: Deploy to Azure
44
+
45
+ on:
46
+ push:
47
+ branches: [main]
48
+
49
+ jobs:
50
+ deploy:
51
+ runs-on: ubuntu-latest
52
+
53
+ steps:
54
+ - uses: actions/checkout@v4
55
+
56
+ - name: Setup Ruby
57
+ uses: ruby/setup-ruby@v1
58
+ with:
59
+ ruby-version: '3.3'
60
+ bundler-cache: true
61
+
62
+ - name: Precompile assets
63
+ run: |
64
+ bundle exec rails assets:precompile
65
+ env:
66
+ RAILS_ENV: production
67
+ SECRET_KEY_BASE: ${{ secrets.SECRET_KEY_BASE }}
68
+
69
+ - name: Deploy to Azure Web App
70
+ uses: azure/webapps-deploy@v2
71
+ with:
72
+ app-name: mi-rails-app
73
+ publish-profile: ${{ secrets.AZURE_WEBAPP_PUBLISH_PROFILE }}
74
+ ```
75
+
76
+ ### Deployment Slots
77
+
78
+ ```bash
79
+ # Crear slot de staging
80
+ az webapp deployment slot create \
81
+ --name mi-rails-app \
82
+ --resource-group rails-app-rg \
83
+ --slot staging
84
+
85
+ # Configurar settings del slot
86
+ az webapp config appsettings set \
87
+ --resource-group rails-app-rg \
88
+ --name mi-rails-app \
89
+ --slot staging \
90
+ --settings RAILS_ENV=staging
91
+
92
+ # Swap slots (zero-downtime deployment)
93
+ az webapp deployment slot swap \
94
+ --resource-group rails-app-rg \
95
+ --name mi-rails-app \
96
+ --slot staging \
97
+ --target-slot production
98
+ ```
99
+
100
+ ### Auto-scaling
101
+
102
+ ```bash
103
+ # Configurar autoscaling
104
+ az monitor autoscale create \
105
+ --resource-group rails-app-rg \
106
+ --resource mi-rails-app \
107
+ --resource-type Microsoft.Web/serverfarms \
108
+ --name rails-autoscale \
109
+ --min-count 1 \
110
+ --max-count 5 \
111
+ --count 2
112
+
113
+ # Regla: escalar si CPU > 70%
114
+ az monitor autoscale rule create \
115
+ --resource-group rails-app-rg \
116
+ --autoscale-name rails-autoscale \
117
+ --condition "Percentage CPU > 70 avg 5m" \
118
+ --scale out 1
119
+ ```
120
+
121
+ ## Blob Storage
122
+
123
+ ### Configurar Active Storage
124
+
125
+ ```ruby
126
+ # Gemfile
127
+ gem "azure-storage-blob", require: false
128
+
129
+ # config/storage.yml
130
+ azure:
131
+ service: AzureStorage
132
+ storage_account_name: <%= ENV["AZURE_STORAGE_ACCOUNT"] %>
133
+ storage_access_key: <%= ENV["AZURE_STORAGE_ACCESS_KEY"] %>
134
+ container: <%= ENV.fetch("AZURE_STORAGE_CONTAINER") { "uploads-#{Rails.env}" } %>
135
+
136
+ # Para uploads públicos
137
+ azure_public:
138
+ service: AzureStorage
139
+ storage_account_name: <%= ENV["AZURE_STORAGE_ACCOUNT"] %>
140
+ storage_access_key: <%= ENV["AZURE_STORAGE_ACCESS_KEY"] %>
141
+ container: public-uploads
142
+ public: true
143
+
144
+ # config/environments/production.rb
145
+ config.active_storage.service = :azure
146
+ ```
147
+
148
+ ### SAS Tokens (Shared Access Signatures)
149
+
150
+ ```ruby
151
+ # app/services/azure_blob_service.rb
152
+ class AzureBlobService
153
+ def initialize
154
+ @account_name = ENV["AZURE_STORAGE_ACCOUNT"]
155
+ @account_key = ENV["AZURE_STORAGE_ACCESS_KEY"]
156
+ @container = ENV["AZURE_STORAGE_CONTAINER"]
157
+
158
+ @client = Azure::Storage::Blob::BlobService.create(
159
+ storage_account_name: @account_name,
160
+ storage_access_key: @account_key
161
+ )
162
+ end
163
+
164
+ # Generar URL con SAS token para subir
165
+ def generate_upload_url(blob_name, content_type:, expires_in: 15.minutes)
166
+ sas_token = generate_sas_token(
167
+ blob_name,
168
+ permissions: "w",
169
+ expires_in: expires_in
170
+ )
171
+
172
+ "https://#{@account_name}.blob.core.windows.net/#{@container}/#{blob_name}?#{sas_token}"
173
+ end
174
+
175
+ # Generar URL con SAS token para descargar
176
+ def generate_download_url(blob_name, expires_in: 1.hour, filename: nil)
177
+ content_disposition = filename ? "attachment; filename=\"#{filename}\"" : nil
178
+
179
+ sas_token = generate_sas_token(
180
+ blob_name,
181
+ permissions: "r",
182
+ expires_in: expires_in,
183
+ content_disposition: content_disposition
184
+ )
185
+
186
+ "https://#{@account_name}.blob.core.windows.net/#{@container}/#{blob_name}?#{sas_token}"
187
+ end
188
+
189
+ private
190
+
191
+ def generate_sas_token(blob_name, permissions:, expires_in:, content_disposition: nil)
192
+ generator = Azure::Storage::Common::Core::Auth::SharedAccessSignature.new(
193
+ @account_name,
194
+ @account_key
195
+ )
196
+
197
+ options = {
198
+ permissions: permissions,
199
+ start: (Time.current - 5.minutes).utc.iso8601,
200
+ expiry: (Time.current + expires_in).utc.iso8601,
201
+ resource: "b"
202
+ }
203
+
204
+ options[:content_disposition] = content_disposition if content_disposition
205
+
206
+ generator.generate_service_sas_token(
207
+ "#{@container}/#{blob_name}",
208
+ options
209
+ )
210
+ end
211
+ end
212
+ ```
213
+
214
+ ### CORS Configuration
215
+
216
+ ```bash
217
+ # Configurar CORS para el storage account
218
+ az storage cors add \
219
+ --account-name mistorageaccount \
220
+ --services b \
221
+ --methods GET PUT POST DELETE \
222
+ --origins "https://miapp.com" "http://localhost:3000" \
223
+ --allowed-headers "*" \
224
+ --exposed-headers "*" \
225
+ --max-age 3600
226
+ ```
227
+
228
+ ## Azure SQL / PostgreSQL
229
+
230
+ ### Configurar Rails con Azure Database for PostgreSQL
231
+
232
+ ```yaml
233
+ # config/database.yml
234
+ production:
235
+ adapter: postgresql
236
+ encoding: unicode
237
+ pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
238
+ database: <%= ENV["AZURE_PG_DATABASE"] %>
239
+ username: <%= ENV["AZURE_PG_USERNAME"] %>
240
+ password: <%= ENV["AZURE_PG_PASSWORD"] %>
241
+ host: <%= ENV["AZURE_PG_HOST"] %>
242
+ port: 5432
243
+ sslmode: require
244
+ ```
245
+
246
+ ```bash
247
+ # Crear servidor PostgreSQL
248
+ az postgres flexible-server create \
249
+ --resource-group rails-app-rg \
250
+ --name rails-pg-server \
251
+ --location eastus \
252
+ --admin-user railsadmin \
253
+ --admin-password 'SecurePassword123!' \
254
+ --sku-name Standard_B1ms \
255
+ --version 15 \
256
+ --storage-size 32
257
+
258
+ # Crear base de datos
259
+ az postgres flexible-server db create \
260
+ --resource-group rails-app-rg \
261
+ --server-name rails-pg-server \
262
+ --database-name rails_production
263
+
264
+ # Configurar firewall (permitir Azure services)
265
+ az postgres flexible-server firewall-rule create \
266
+ --resource-group rails-app-rg \
267
+ --name rails-pg-server \
268
+ --rule-name AllowAzureServices \
269
+ --start-ip-address 0.0.0.0 \
270
+ --end-ip-address 0.0.0.0
271
+ ```
272
+
273
+ ### Backups y Restauración
274
+
275
+ ```bash
276
+ # Los backups son automáticos en Azure PostgreSQL
277
+ # Configurar retención (7-35 días)
278
+ az postgres flexible-server update \
279
+ --resource-group rails-app-rg \
280
+ --name rails-pg-server \
281
+ --backup-retention 14
282
+
283
+ # Restaurar a punto en el tiempo
284
+ az postgres flexible-server restore \
285
+ --resource-group rails-app-rg \
286
+ --name rails-pg-server-restored \
287
+ --source-server rails-pg-server \
288
+ --restore-time "2024-01-15T10:00:00Z"
289
+ ```
290
+
291
+ ## Azure Functions
292
+
293
+ ### HTTP Trigger para Webhooks
294
+
295
+ ```ruby
296
+ # function.json
297
+ {
298
+ "bindings": [
299
+ {
300
+ "authLevel": "function",
301
+ "type": "httpTrigger",
302
+ "direction": "in",
303
+ "name": "req",
304
+ "methods": ["post"]
305
+ },
306
+ {
307
+ "type": "http",
308
+ "direction": "out",
309
+ "name": "$return"
310
+ },
311
+ {
312
+ "type": "queue",
313
+ "direction": "out",
314
+ "name": "webhookQueue",
315
+ "queueName": "webhooks",
316
+ "connection": "AzureWebJobsStorage"
317
+ }
318
+ ]
319
+ }
320
+
321
+ # handler.rb (con Ruby custom handler)
322
+ require "json"
323
+ require "sinatra"
324
+
325
+ post "/api/webhook" do
326
+ content_type :json
327
+ payload = JSON.parse(request.body.read)
328
+
329
+ # Encolar para procesamiento
330
+ {
331
+ statusCode: 200,
332
+ body: { received: true }.to_json,
333
+ webhookQueue: payload.to_json
334
+ }.to_json
335
+ end
336
+ ```
337
+
338
+ ### Timer Trigger para Tareas Programadas
339
+
340
+ ```json
341
+ {
342
+ "bindings": [
343
+ {
344
+ "name": "timer",
345
+ "type": "timerTrigger",
346
+ "direction": "in",
347
+ "schedule": "0 0 * * * *"
348
+ }
349
+ ]
350
+ }
351
+ ```
352
+
353
+ ## Container Instances / AKS
354
+
355
+ ### Azure Container Instances (Simple)
356
+
357
+ ```bash
358
+ # Crear container instance
359
+ az container create \
360
+ --resource-group rails-app-rg \
361
+ --name rails-app \
362
+ --image miregistry.azurecr.io/rails-app:latest \
363
+ --cpu 1 \
364
+ --memory 1.5 \
365
+ --ports 3000 \
366
+ --environment-variables \
367
+ RAILS_ENV=production \
368
+ RAILS_LOG_TO_STDOUT=true \
369
+ --secure-environment-variables \
370
+ DATABASE_URL='postgresql://...' \
371
+ SECRET_KEY_BASE='...'
372
+ ```
373
+
374
+ ### Azure Kubernetes Service (AKS)
375
+
376
+ ```yaml
377
+ # kubernetes/deployment.yaml
378
+ apiVersion: apps/v1
379
+ kind: Deployment
380
+ metadata:
381
+ name: rails-app
382
+ spec:
383
+ replicas: 3
384
+ selector:
385
+ matchLabels:
386
+ app: rails-app
387
+ template:
388
+ metadata:
389
+ labels:
390
+ app: rails-app
391
+ spec:
392
+ containers:
393
+ - name: rails-app
394
+ image: miregistry.azurecr.io/rails-app:latest
395
+ ports:
396
+ - containerPort: 3000
397
+ env:
398
+ - name: RAILS_ENV
399
+ value: production
400
+ - name: DATABASE_URL
401
+ valueFrom:
402
+ secretKeyRef:
403
+ name: rails-secrets
404
+ key: database-url
405
+ - name: SECRET_KEY_BASE
406
+ valueFrom:
407
+ secretKeyRef:
408
+ name: rails-secrets
409
+ key: secret-key-base
410
+ resources:
411
+ requests:
412
+ memory: "512Mi"
413
+ cpu: "250m"
414
+ limits:
415
+ memory: "1Gi"
416
+ cpu: "500m"
417
+ livenessProbe:
418
+ httpGet:
419
+ path: /up
420
+ port: 3000
421
+ initialDelaySeconds: 30
422
+ periodSeconds: 10
423
+ readinessProbe:
424
+ httpGet:
425
+ path: /up
426
+ port: 3000
427
+ initialDelaySeconds: 5
428
+ periodSeconds: 5
429
+ ---
430
+ apiVersion: v1
431
+ kind: Service
432
+ metadata:
433
+ name: rails-app-service
434
+ spec:
435
+ type: LoadBalancer
436
+ ports:
437
+ - port: 80
438
+ targetPort: 3000
439
+ selector:
440
+ app: rails-app
441
+ ```
442
+
443
+ ```bash
444
+ # Crear cluster AKS
445
+ az aks create \
446
+ --resource-group rails-app-rg \
447
+ --name rails-aks \
448
+ --node-count 2 \
449
+ --node-vm-size Standard_B2s \
450
+ --generate-ssh-keys
451
+
452
+ # Obtener credenciales
453
+ az aks get-credentials \
454
+ --resource-group rails-app-rg \
455
+ --name rails-aks
456
+
457
+ # Desplegar
458
+ kubectl apply -f kubernetes/
459
+ ```
460
+
461
+ ## Azure CDN
462
+
463
+ ### Configurar CDN para Assets
464
+
465
+ ```bash
466
+ # Crear perfil CDN
467
+ az cdn profile create \
468
+ --resource-group rails-app-rg \
469
+ --name rails-cdn-profile \
470
+ --sku Standard_Microsoft
471
+
472
+ # Crear endpoint
473
+ az cdn endpoint create \
474
+ --resource-group rails-app-rg \
475
+ --profile-name rails-cdn-profile \
476
+ --name rails-cdn \
477
+ --origin mi-rails-app.azurewebsites.net \
478
+ --origin-host-header mi-rails-app.azurewebsites.net
479
+ ```
480
+
481
+ ```ruby
482
+ # config/environments/production.rb
483
+ config.asset_host = ENV["AZURE_CDN_ENDPOINT"]
484
+ # Ejemplo: "https://rails-cdn.azureedge.net"
485
+ ```
486
+
487
+ ### Purge Cache
488
+
489
+ ```bash
490
+ # Purgar todo el contenido
491
+ az cdn endpoint purge \
492
+ --resource-group rails-app-rg \
493
+ --profile-name rails-cdn-profile \
494
+ --name rails-cdn \
495
+ --content-paths "/*"
496
+
497
+ # Purgar assets específicos
498
+ az cdn endpoint purge \
499
+ --resource-group rails-app-rg \
500
+ --profile-name rails-cdn-profile \
501
+ --name rails-cdn \
502
+ --content-paths "/assets/*"
503
+ ```
504
+
505
+ ## DNS Zones
506
+
507
+ ### Configurar DNS
508
+
509
+ ```bash
510
+ # Crear zona DNS
511
+ az network dns zone create \
512
+ --resource-group rails-app-rg \
513
+ --name midominio.com
514
+
515
+ # Agregar registro A
516
+ az network dns record-set a add-record \
517
+ --resource-group rails-app-rg \
518
+ --zone-name midominio.com \
519
+ --record-set-name app \
520
+ --ipv4-address 20.xx.xx.xx
521
+
522
+ # Agregar CNAME para CDN
523
+ az network dns record-set cname set-record \
524
+ --resource-group rails-app-rg \
525
+ --zone-name midominio.com \
526
+ --record-set-name cdn \
527
+ --cname rails-cdn.azureedge.net
528
+ ```
529
+
530
+ ### Traffic Manager (Load Balancing Global)
531
+
532
+ ```bash
533
+ # Crear perfil Traffic Manager
534
+ az network traffic-manager profile create \
535
+ --resource-group rails-app-rg \
536
+ --name rails-traffic-manager \
537
+ --routing-method Performance \
538
+ --unique-dns-name rails-app-tm
539
+
540
+ # Agregar endpoints
541
+ az network traffic-manager endpoint create \
542
+ --resource-group rails-app-rg \
543
+ --profile-name rails-traffic-manager \
544
+ --name eastus-endpoint \
545
+ --type azureEndpoints \
546
+ --target-resource-id /subscriptions/.../sites/rails-app-eastus \
547
+ --endpoint-status Enabled
548
+
549
+ az network traffic-manager endpoint create \
550
+ --resource-group rails-app-rg \
551
+ --profile-name rails-traffic-manager \
552
+ --name westus-endpoint \
553
+ --type azureEndpoints \
554
+ --target-resource-id /subscriptions/.../sites/rails-app-westus \
555
+ --endpoint-status Enabled
556
+ ```
557
+
558
+ ## Azure AD
559
+
560
+ ### Configurar OAuth con Azure AD
561
+
562
+ ```ruby
563
+ # Gemfile
564
+ gem "omniauth-azure-activedirectory-v2"
565
+
566
+ # config/initializers/omniauth.rb
567
+ Rails.application.config.middleware.use OmniAuth::Builder do
568
+ provider :azure_activedirectory_v2,
569
+ client_id: ENV["AZURE_AD_CLIENT_ID"],
570
+ client_secret: ENV["AZURE_AD_CLIENT_SECRET"],
571
+ tenant_id: ENV["AZURE_AD_TENANT_ID"]
572
+ end
573
+
574
+ # app/controllers/sessions_controller.rb
575
+ class SessionsController < ApplicationController
576
+ def azure_callback
577
+ auth = request.env["omniauth.auth"]
578
+
579
+ user = User.find_or_create_by(azure_id: auth.uid) do |u|
580
+ u.email = auth.info.email
581
+ u.name = auth.info.name
582
+ end
583
+
584
+ session[:user_id] = user.id
585
+ redirect_to root_path, notice: "Sesion iniciada"
586
+ end
587
+
588
+ def failure
589
+ redirect_to root_path, alert: "Error de autenticacion"
590
+ end
591
+ end
592
+
593
+ # config/routes.rb
594
+ get "/auth/azure_activedirectory_v2/callback", to: "sessions#azure_callback"
595
+ get "/auth/failure", to: "sessions#failure"
596
+ ```
597
+
598
+ ### Managed Identity
599
+
600
+ ```ruby
601
+ # Para acceder a recursos Azure sin credenciales hardcodeadas
602
+ # app/services/azure_managed_identity.rb
603
+ class AzureManagedIdentity
604
+ def initialize
605
+ @token_endpoint = "http://169.254.169.254/metadata/identity/oauth2/token"
606
+ end
607
+
608
+ def get_token(resource:)
609
+ response = HTTParty.get(
610
+ @token_endpoint,
611
+ query: {
612
+ "api-version" => "2019-08-01",
613
+ resource: resource
614
+ },
615
+ headers: { "Metadata" => "true" }
616
+ )
617
+
618
+ JSON.parse(response.body)["access_token"]
619
+ end
620
+
621
+ def get_secret(vault_name:, secret_name:)
622
+ token = get_token(resource: "https://vault.azure.net")
623
+
624
+ response = HTTParty.get(
625
+ "https://#{vault_name}.vault.azure.net/secrets/#{secret_name}?api-version=7.4",
626
+ headers: { "Authorization" => "Bearer #{token}" }
627
+ )
628
+
629
+ JSON.parse(response.body)["value"]
630
+ end
631
+ end
632
+ ```
633
+
634
+ ## Redis Cache
635
+
636
+ ### Configurar con Rails
637
+
638
+ ```ruby
639
+ # Gemfile
640
+ gem "redis"
641
+ gem "hiredis"
642
+
643
+ # config/initializers/redis.rb
644
+ REDIS = Redis.new(
645
+ url: ENV.fetch("AZURE_REDIS_URL") { "redis://localhost:6379/0" },
646
+ ssl: Rails.env.production?,
647
+ ssl_params: { verify_mode: OpenSSL::SSL::VERIFY_NONE }
648
+ )
649
+
650
+ # config/environments/production.rb
651
+ config.cache_store = :redis_cache_store, {
652
+ url: ENV["AZURE_REDIS_URL"],
653
+ ssl_params: { verify_mode: OpenSSL::SSL::VERIFY_NONE }
654
+ }
655
+
656
+ # Session store
657
+ config.session_store :redis_store,
658
+ servers: [{
659
+ url: ENV["AZURE_REDIS_URL"],
660
+ ssl: true,
661
+ ssl_params: { verify_mode: OpenSSL::SSL::VERIFY_NONE }
662
+ }],
663
+ expire_after: 1.day
664
+ ```
665
+
666
+ ```bash
667
+ # Crear Azure Cache for Redis
668
+ az redis create \
669
+ --resource-group rails-app-rg \
670
+ --name rails-redis-cache \
671
+ --location eastus \
672
+ --sku Basic \
673
+ --vm-size c0
674
+
675
+ # Obtener connection string
676
+ az redis list-keys \
677
+ --resource-group rails-app-rg \
678
+ --name rails-redis-cache
679
+ ```
680
+
681
+ ## SendGrid / Communication Services
682
+
683
+ ### Configurar Email con SendGrid (via Azure)
684
+
685
+ ```ruby
686
+ # Gemfile
687
+ gem "sendgrid-ruby"
688
+
689
+ # config/environments/production.rb
690
+ config.action_mailer.delivery_method = :smtp
691
+ config.action_mailer.smtp_settings = {
692
+ address: "smtp.sendgrid.net",
693
+ port: 587,
694
+ domain: "midominio.com",
695
+ user_name: "apikey",
696
+ password: ENV["SENDGRID_API_KEY"],
697
+ authentication: :plain,
698
+ enable_starttls_auto: true
699
+ }
700
+ ```
701
+
702
+ ### Azure Communication Services (Email)
703
+
704
+ ```ruby
705
+ # app/services/azure_email_service.rb
706
+ class AzureEmailService
707
+ def initialize
708
+ @endpoint = ENV["AZURE_COMMUNICATION_ENDPOINT"]
709
+ @access_key = ENV["AZURE_COMMUNICATION_KEY"]
710
+ end
711
+
712
+ def send_email(to:, subject:, body:, from: "noreply@midominio.com")
713
+ uri = URI("#{@endpoint}/emails:send?api-version=2023-03-31")
714
+
715
+ request = Net::HTTP::Post.new(uri)
716
+ request["Content-Type"] = "application/json"
717
+ request["Authorization"] = generate_auth_header("POST", uri.path)
718
+
719
+ request.body = {
720
+ senderAddress: from,
721
+ recipients: {
722
+ to: [{ address: to }]
723
+ },
724
+ content: {
725
+ subject: subject,
726
+ plainText: body
727
+ }
728
+ }.to_json
729
+
730
+ http = Net::HTTP.new(uri.host, uri.port)
731
+ http.use_ssl = true
732
+ http.request(request)
733
+ end
734
+
735
+ private
736
+
737
+ def generate_auth_header(method, path)
738
+ # Implementar HMAC-SHA256 auth
739
+ # ...
740
+ end
741
+ end
742
+ ```
743
+
744
+ ## Key Vault
745
+
746
+ ### Cargar Secrets al Iniciar
747
+
748
+ ```ruby
749
+ # config/initializers/azure_key_vault.rb
750
+ if Rails.env.production?
751
+ require "azure/key_vault"
752
+
753
+ client = Azure::KeyVault::KeyVaultClient.new
754
+
755
+ # Con Managed Identity
756
+ vault_url = "https://rails-app-vault.vault.azure.net"
757
+
758
+ secrets = %w[DATABASE_URL SECRET_KEY_BASE REDIS_URL]
759
+
760
+ secrets.each do |secret_name|
761
+ begin
762
+ secret = client.get_secret(vault_url, secret_name.downcase.tr("_", "-"), "")
763
+ ENV[secret_name] = secret.value unless ENV[secret_name].present?
764
+ rescue StandardError => e
765
+ Rails.logger.warn "Could not load secret #{secret_name}: #{e.message}"
766
+ end
767
+ end
768
+ end
769
+ ```
770
+
771
+ ```bash
772
+ # Crear Key Vault
773
+ az keyvault create \
774
+ --resource-group rails-app-rg \
775
+ --name rails-app-vault \
776
+ --location eastus
777
+
778
+ # Agregar secreto
779
+ az keyvault secret set \
780
+ --vault-name rails-app-vault \
781
+ --name "database-url" \
782
+ --value "postgresql://..."
783
+
784
+ # Dar acceso a la App Service
785
+ az keyvault set-policy \
786
+ --name rails-app-vault \
787
+ --object-id $(az webapp identity show --name mi-rails-app --resource-group rails-app-rg --query principalId -o tsv) \
788
+ --secret-permissions get list
789
+ ```
790
+
791
+ ## Application Insights
792
+
793
+ ### Configurar Monitoring
794
+
795
+ ```ruby
796
+ # Gemfile
797
+ gem "application_insights"
798
+
799
+ # config/initializers/application_insights.rb
800
+ if Rails.env.production?
801
+ require "application_insights"
802
+
803
+ ApplicationInsights::TelemetryClient.new(ENV["AZURE_APP_INSIGHTS_KEY"])
804
+
805
+ # Middleware para tracking automático
806
+ Rails.application.config.middleware.use(
807
+ ApplicationInsights::Rack::TrackRequest,
808
+ ENV["AZURE_APP_INSIGHTS_KEY"]
809
+ )
810
+ end
811
+
812
+ # app/services/azure_insights.rb
813
+ class AzureInsights
814
+ def initialize
815
+ @client = ApplicationInsights::TelemetryClient.new(
816
+ ENV["AZURE_APP_INSIGHTS_KEY"]
817
+ )
818
+ end
819
+
820
+ def track_event(name, properties = {}, measurements = {})
821
+ @client.track_event(name, properties, measurements)
822
+ @client.flush
823
+ end
824
+
825
+ def track_exception(exception, properties = {})
826
+ @client.track_exception(exception, properties)
827
+ @client.flush
828
+ end
829
+
830
+ def track_metric(name, value, properties = {})
831
+ @client.track_metric(name, value, properties: properties)
832
+ @client.flush
833
+ end
834
+
835
+ def track_request(name, url, success, start_time, duration)
836
+ @client.track_request(name, url, start_time, duration, "200", success)
837
+ @client.flush
838
+ end
839
+ end
840
+ ```
841
+
842
+ ### Custom Logging
843
+
844
+ ```ruby
845
+ # app/middleware/azure_logging.rb
846
+ class AzureLogging
847
+ def initialize(app)
848
+ @app = app
849
+ @insights = AzureInsights.new
850
+ end
851
+
852
+ def call(env)
853
+ start_time = Time.current
854
+
855
+ begin
856
+ status, headers, response = @app.call(env)
857
+
858
+ duration = ((Time.current - start_time) * 1000).round
859
+ @insights.track_request(
860
+ env["PATH_INFO"],
861
+ env["REQUEST_URI"] || env["PATH_INFO"],
862
+ status < 400,
863
+ start_time,
864
+ duration
865
+ )
866
+
867
+ [status, headers, response]
868
+ rescue StandardError => e
869
+ @insights.track_exception(e)
870
+ raise
871
+ end
872
+ end
873
+ end
874
+ ```
875
+
876
+ ## Costos y Optimización
877
+
878
+ ### Calculadora de Costos
879
+
880
+ ```markdown
881
+ ## Estimación mensual para app Rails pequeña-mediana
882
+
883
+ | Servicio | Configuración | Costo/mes |
884
+ |----------|--------------|-----------|
885
+ | App Service | B1 (1 core, 1.75GB) | ~$55 |
886
+ | PostgreSQL | B1ms (1 vCore, 2GB) | ~$25 |
887
+ | Redis Cache | C0 (250MB) | ~$16 |
888
+ | Blob Storage | 50GB + transacciones | ~$5 |
889
+ | CDN | 100GB transferencia | ~$8 |
890
+ | DNS Zone | 1 zona | ~$0.50 |
891
+ | **Total** | | **~$110/mes** |
892
+
893
+ ## Optimizaciones:
894
+ - Reserved Instances (1-3 años): hasta 72% ahorro
895
+ - Azure Hybrid Benefit: si tienes licencias Windows
896
+ - Dev/Test pricing: para ambientes no-producción
897
+ - Autoscaling: reducir en horas bajas
898
+ ```
899
+
900
+ ### Reserved Instances
901
+
902
+ ```bash
903
+ # Ver recomendaciones de reservas
904
+ az consumption reservation recommendation list \
905
+ --scope "subscriptions/xxx-xxx-xxx"
906
+
907
+ # Comprar reserva
908
+ az reservations reservation-order purchase \
909
+ --sku Standard_B2s \
910
+ --term P1Y \
911
+ --billing-plan Monthly \
912
+ --quantity 2
913
+ ```
914
+
915
+ ## Checklist de Deployment
916
+
917
+ - [ ] Resource Group creado
918
+ - [ ] App Service Plan configurado
919
+ - [ ] Web App con Ruby runtime
920
+ - [ ] PostgreSQL/MySQL provisionado
921
+ - [ ] Blob Storage para archivos
922
+ - [ ] Redis Cache para sessions/cache
923
+ - [ ] Key Vault para secrets
924
+ - [ ] Managed Identity habilitado
925
+ - [ ] Application Insights configurado
926
+ - [ ] CDN para assets estáticos
927
+ - [ ] DNS configurado
928
+ - [ ] SSL/TLS con App Service Managed Certificate
929
+ - [ ] Deployment slots para zero-downtime
930
+ - [ ] Backups automáticos verificados
931
+ - [ ] Alertas configuradas