autoworkflow 3.1.4 → 3.5.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.
- package/.claude/commands/analyze.md +19 -0
- package/.claude/commands/audit.md +174 -11
- package/.claude/commands/build.md +39 -0
- package/.claude/commands/commit.md +25 -0
- package/.claude/commands/fix.md +23 -0
- package/.claude/commands/plan.md +18 -0
- package/.claude/commands/suggest.md +23 -0
- package/.claude/commands/verify.md +18 -0
- package/.claude/hooks/post-bash-router.sh +20 -0
- package/.claude/hooks/post-commit.sh +140 -0
- package/.claude/hooks/pre-edit.sh +129 -0
- package/.claude/hooks/session-check.sh +79 -0
- package/.claude/settings.json +40 -6
- package/.claude/settings.local.json +3 -1
- package/.claude/skills/actix.md +337 -0
- package/.claude/skills/alembic.md +504 -0
- package/.claude/skills/angular.md +237 -0
- package/.claude/skills/api-design.md +187 -0
- package/.claude/skills/aspnet-core.md +377 -0
- package/.claude/skills/astro.md +245 -0
- package/.claude/skills/auth-clerk.md +327 -0
- package/.claude/skills/auth-firebase.md +367 -0
- package/.claude/skills/auth-nextauth.md +359 -0
- package/.claude/skills/auth-supabase.md +368 -0
- package/.claude/skills/axum.md +386 -0
- package/.claude/skills/blazor.md +456 -0
- package/.claude/skills/chi.md +348 -0
- package/.claude/skills/code-review.md +133 -0
- package/.claude/skills/csharp.md +296 -0
- package/.claude/skills/css-modules.md +325 -0
- package/.claude/skills/cypress.md +343 -0
- package/.claude/skills/debugging.md +133 -0
- package/.claude/skills/diesel.md +392 -0
- package/.claude/skills/django.md +301 -0
- package/.claude/skills/docker.md +319 -0
- package/.claude/skills/doctrine.md +473 -0
- package/.claude/skills/documentation.md +182 -0
- package/.claude/skills/dotnet.md +409 -0
- package/.claude/skills/drizzle.md +293 -0
- package/.claude/skills/echo.md +321 -0
- package/.claude/skills/eloquent.md +256 -0
- package/.claude/skills/emotion.md +426 -0
- package/.claude/skills/entity-framework.md +370 -0
- package/.claude/skills/express.md +316 -0
- package/.claude/skills/fastapi.md +329 -0
- package/.claude/skills/fastify.md +299 -0
- package/.claude/skills/fiber.md +315 -0
- package/.claude/skills/flask.md +322 -0
- package/.claude/skills/gin.md +342 -0
- package/.claude/skills/git.md +116 -0
- package/.claude/skills/github-actions.md +353 -0
- package/.claude/skills/go.md +377 -0
- package/.claude/skills/gorm.md +409 -0
- package/.claude/skills/graphql.md +478 -0
- package/.claude/skills/hibernate.md +379 -0
- package/.claude/skills/hono.md +306 -0
- package/.claude/skills/java.md +400 -0
- package/.claude/skills/jest.md +313 -0
- package/.claude/skills/jpa.md +282 -0
- package/.claude/skills/kotlin.md +347 -0
- package/.claude/skills/kubernetes.md +363 -0
- package/.claude/skills/laravel.md +414 -0
- package/.claude/skills/mcp-browser.md +320 -0
- package/.claude/skills/mcp-database.md +219 -0
- package/.claude/skills/mcp-fetch.md +241 -0
- package/.claude/skills/mcp-filesystem.md +204 -0
- package/.claude/skills/mcp-github.md +217 -0
- package/.claude/skills/mcp-memory.md +240 -0
- package/.claude/skills/mcp-search.md +218 -0
- package/.claude/skills/mcp-slack.md +262 -0
- package/.claude/skills/micronaut.md +388 -0
- package/.claude/skills/mongodb.md +319 -0
- package/.claude/skills/mongoose.md +355 -0
- package/.claude/skills/mysql.md +281 -0
- package/.claude/skills/nestjs.md +335 -0
- package/.claude/skills/nextjs-app-router.md +260 -0
- package/.claude/skills/nextjs-pages.md +172 -0
- package/.claude/skills/nuxt.md +202 -0
- package/.claude/skills/openapi.md +489 -0
- package/.claude/skills/performance.md +199 -0
- package/.claude/skills/php.md +398 -0
- package/.claude/skills/playwright.md +371 -0
- package/.claude/skills/postgresql.md +257 -0
- package/.claude/skills/prisma.md +293 -0
- package/.claude/skills/pydantic.md +304 -0
- package/.claude/skills/pytest.md +313 -0
- package/.claude/skills/python.md +272 -0
- package/.claude/skills/quarkus.md +377 -0
- package/.claude/skills/react.md +230 -0
- package/.claude/skills/redis.md +391 -0
- package/.claude/skills/refactoring.md +143 -0
- package/.claude/skills/remix.md +246 -0
- package/.claude/skills/rest-api.md +490 -0
- package/.claude/skills/rocket.md +366 -0
- package/.claude/skills/rust.md +341 -0
- package/.claude/skills/sass.md +380 -0
- package/.claude/skills/sea-orm.md +382 -0
- package/.claude/skills/security.md +167 -0
- package/.claude/skills/sequelize.md +395 -0
- package/.claude/skills/spring-boot.md +416 -0
- package/.claude/skills/sqlalchemy.md +269 -0
- package/.claude/skills/sqlx-rust.md +408 -0
- package/.claude/skills/state-jotai.md +346 -0
- package/.claude/skills/state-mobx.md +353 -0
- package/.claude/skills/state-pinia.md +431 -0
- package/.claude/skills/state-redux.md +337 -0
- package/.claude/skills/state-tanstack-query.md +434 -0
- package/.claude/skills/state-zustand.md +340 -0
- package/.claude/skills/styled-components.md +403 -0
- package/.claude/skills/svelte.md +238 -0
- package/.claude/skills/sveltekit.md +207 -0
- package/.claude/skills/symfony.md +437 -0
- package/.claude/skills/tailwind.md +279 -0
- package/.claude/skills/terraform.md +394 -0
- package/.claude/skills/testing-library.md +371 -0
- package/.claude/skills/trpc.md +426 -0
- package/.claude/skills/typeorm.md +368 -0
- package/.claude/skills/vitest.md +330 -0
- package/.claude/skills/vue.md +202 -0
- package/.claude/skills/warp.md +365 -0
- package/README.md +135 -52
- package/package.json +1 -1
- package/system/triggers.md +152 -11
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
# Tailwind CSS Skill
|
|
2
|
+
|
|
3
|
+
## Layout Patterns
|
|
4
|
+
\`\`\`tsx
|
|
5
|
+
// Flexbox
|
|
6
|
+
<div className="flex items-center justify-between gap-4">
|
|
7
|
+
<div className="flex flex-col md:flex-row">
|
|
8
|
+
<div className="flex-1"> {/* Grow to fill */}
|
|
9
|
+
<div className="flex-shrink-0"> {/* Don't shrink */}
|
|
10
|
+
|
|
11
|
+
// Grid
|
|
12
|
+
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
|
13
|
+
<div className="grid grid-cols-12">
|
|
14
|
+
<div className="col-span-8">Main</div>
|
|
15
|
+
<div className="col-span-4">Sidebar</div>
|
|
16
|
+
</div>
|
|
17
|
+
|
|
18
|
+
// Container
|
|
19
|
+
<div className="container mx-auto px-4">
|
|
20
|
+
<div className="max-w-7xl mx-auto">
|
|
21
|
+
|
|
22
|
+
// Centering
|
|
23
|
+
<div className="flex items-center justify-center min-h-screen">
|
|
24
|
+
<div className="grid place-items-center">
|
|
25
|
+
\`\`\`
|
|
26
|
+
|
|
27
|
+
## Responsive Design (Mobile-First)
|
|
28
|
+
\`\`\`tsx
|
|
29
|
+
// Breakpoints: sm(640) md(768) lg(1024) xl(1280) 2xl(1536)
|
|
30
|
+
<div className="
|
|
31
|
+
w-full // Mobile: full width
|
|
32
|
+
md:w-1/2 // Tablet: half width
|
|
33
|
+
lg:w-1/3 // Desktop: third width
|
|
34
|
+
">
|
|
35
|
+
|
|
36
|
+
// Hide/show at breakpoints
|
|
37
|
+
<div className="hidden md:block">Desktop only</div>
|
|
38
|
+
<div className="md:hidden">Mobile only</div>
|
|
39
|
+
|
|
40
|
+
// Responsive text
|
|
41
|
+
<h1 className="text-2xl md:text-4xl lg:text-6xl">
|
|
42
|
+
|
|
43
|
+
// Responsive spacing
|
|
44
|
+
<div className="p-4 md:p-6 lg:p-8">
|
|
45
|
+
<div className="space-y-4 md:space-y-6">
|
|
46
|
+
\`\`\`
|
|
47
|
+
|
|
48
|
+
## Interactive States
|
|
49
|
+
\`\`\`tsx
|
|
50
|
+
// Button with states
|
|
51
|
+
<button className="
|
|
52
|
+
bg-blue-500 text-white
|
|
53
|
+
hover:bg-blue-600
|
|
54
|
+
focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2
|
|
55
|
+
active:bg-blue-700
|
|
56
|
+
disabled:opacity-50 disabled:cursor-not-allowed
|
|
57
|
+
transition-colors duration-200
|
|
58
|
+
">
|
|
59
|
+
|
|
60
|
+
// Link states
|
|
61
|
+
<a className="
|
|
62
|
+
text-blue-600
|
|
63
|
+
hover:text-blue-800 hover:underline
|
|
64
|
+
visited:text-purple-600
|
|
65
|
+
">
|
|
66
|
+
|
|
67
|
+
// Form input states
|
|
68
|
+
<input className="
|
|
69
|
+
border border-gray-300 rounded-md
|
|
70
|
+
focus:border-blue-500 focus:ring-1 focus:ring-blue-500
|
|
71
|
+
invalid:border-red-500 invalid:ring-red-500
|
|
72
|
+
placeholder:text-gray-400
|
|
73
|
+
"/>
|
|
74
|
+
|
|
75
|
+
// Group hover (parent hover affects children)
|
|
76
|
+
<div className="group">
|
|
77
|
+
<span className="group-hover:text-blue-600">Hover parent</span>
|
|
78
|
+
</div>
|
|
79
|
+
|
|
80
|
+
// Peer (sibling states)
|
|
81
|
+
<input className="peer" />
|
|
82
|
+
<span className="peer-invalid:text-red-500">Error message</span>
|
|
83
|
+
\`\`\`
|
|
84
|
+
|
|
85
|
+
## Dark Mode
|
|
86
|
+
\`\`\`tsx
|
|
87
|
+
// Class-based dark mode (default)
|
|
88
|
+
<div className="bg-white dark:bg-gray-900 text-gray-900 dark:text-white">
|
|
89
|
+
|
|
90
|
+
// Card with dark mode
|
|
91
|
+
<div className="
|
|
92
|
+
bg-white dark:bg-gray-800
|
|
93
|
+
border border-gray-200 dark:border-gray-700
|
|
94
|
+
shadow-md dark:shadow-gray-900/50
|
|
95
|
+
rounded-lg p-6
|
|
96
|
+
">
|
|
97
|
+
|
|
98
|
+
// Toggle dark mode (in tailwind.config.js: darkMode: 'class')
|
|
99
|
+
// Add/remove 'dark' class on <html> element
|
|
100
|
+
\`\`\`
|
|
101
|
+
|
|
102
|
+
## Typography
|
|
103
|
+
\`\`\`tsx
|
|
104
|
+
// Headings
|
|
105
|
+
<h1 className="text-4xl font-bold tracking-tight">
|
|
106
|
+
<h2 className="text-2xl font-semibold">
|
|
107
|
+
|
|
108
|
+
// Body text
|
|
109
|
+
<p className="text-base text-gray-600 leading-relaxed">
|
|
110
|
+
<p className="text-sm text-gray-500">
|
|
111
|
+
|
|
112
|
+
// Truncation
|
|
113
|
+
<p className="truncate">Single line truncate...</p>
|
|
114
|
+
<p className="line-clamp-3">Multi-line truncate...</p>
|
|
115
|
+
|
|
116
|
+
// Text utilities
|
|
117
|
+
<span className="uppercase tracking-wide">
|
|
118
|
+
<span className="capitalize">
|
|
119
|
+
<span className="font-mono">
|
|
120
|
+
\`\`\`
|
|
121
|
+
|
|
122
|
+
## Common Components
|
|
123
|
+
\`\`\`tsx
|
|
124
|
+
// Card
|
|
125
|
+
<div className="bg-white rounded-lg shadow-md p-6 hover:shadow-lg transition-shadow">
|
|
126
|
+
|
|
127
|
+
// Badge
|
|
128
|
+
<span className="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-blue-100 text-blue-800">
|
|
129
|
+
|
|
130
|
+
// Avatar
|
|
131
|
+
<img className="h-10 w-10 rounded-full object-cover" />
|
|
132
|
+
|
|
133
|
+
// Input
|
|
134
|
+
<input className="
|
|
135
|
+
w-full px-3 py-2
|
|
136
|
+
border border-gray-300 rounded-md
|
|
137
|
+
focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent
|
|
138
|
+
" />
|
|
139
|
+
|
|
140
|
+
// Button variants
|
|
141
|
+
<button className="px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700">
|
|
142
|
+
<button className="px-4 py-2 border border-gray-300 rounded-md hover:bg-gray-50">
|
|
143
|
+
<button className="px-4 py-2 text-blue-600 hover:text-blue-800 hover:underline">
|
|
144
|
+
\`\`\`
|
|
145
|
+
|
|
146
|
+
## Animation & Transitions
|
|
147
|
+
\`\`\`tsx
|
|
148
|
+
// Transitions
|
|
149
|
+
<div className="transition-all duration-300 ease-in-out">
|
|
150
|
+
<div className="transition-colors duration-200">
|
|
151
|
+
<div className="transition-transform hover:scale-105">
|
|
152
|
+
|
|
153
|
+
// Built-in animations
|
|
154
|
+
<div className="animate-spin">Loading...</div>
|
|
155
|
+
<div className="animate-pulse">Loading skeleton</div>
|
|
156
|
+
<div className="animate-bounce">Attention</div>
|
|
157
|
+
|
|
158
|
+
// Transform utilities
|
|
159
|
+
<div className="hover:scale-105 hover:-translate-y-1 transition-transform">
|
|
160
|
+
<div className="rotate-45">
|
|
161
|
+
<div className="skew-x-12">
|
|
162
|
+
\`\`\`
|
|
163
|
+
|
|
164
|
+
## Custom Configuration (tailwind.config.js)
|
|
165
|
+
\`\`\`javascript
|
|
166
|
+
module.exports = {
|
|
167
|
+
content: ['./src/**/*.{js,ts,jsx,tsx}'],
|
|
168
|
+
darkMode: 'class',
|
|
169
|
+
theme: {
|
|
170
|
+
extend: {
|
|
171
|
+
colors: {
|
|
172
|
+
primary: {
|
|
173
|
+
50: '#eff6ff',
|
|
174
|
+
500: '#3b82f6',
|
|
175
|
+
600: '#2563eb',
|
|
176
|
+
700: '#1d4ed8',
|
|
177
|
+
},
|
|
178
|
+
brand: '#ff6b6b',
|
|
179
|
+
},
|
|
180
|
+
fontFamily: {
|
|
181
|
+
sans: ['Inter', 'system-ui', 'sans-serif'],
|
|
182
|
+
},
|
|
183
|
+
spacing: {
|
|
184
|
+
'128': '32rem',
|
|
185
|
+
},
|
|
186
|
+
animation: {
|
|
187
|
+
'fade-in': 'fadeIn 0.5s ease-out',
|
|
188
|
+
},
|
|
189
|
+
keyframes: {
|
|
190
|
+
fadeIn: {
|
|
191
|
+
'0%': { opacity: '0' },
|
|
192
|
+
'100%': { opacity: '1' },
|
|
193
|
+
},
|
|
194
|
+
},
|
|
195
|
+
},
|
|
196
|
+
},
|
|
197
|
+
plugins: [
|
|
198
|
+
require('@tailwindcss/forms'),
|
|
199
|
+
require('@tailwindcss/typography'),
|
|
200
|
+
require('@tailwindcss/aspect-ratio'),
|
|
201
|
+
],
|
|
202
|
+
};
|
|
203
|
+
\`\`\`
|
|
204
|
+
|
|
205
|
+
## Class Variance Authority (CVA)
|
|
206
|
+
\`\`\`tsx
|
|
207
|
+
// For type-safe component variants
|
|
208
|
+
import { cva, type VariantProps } from 'class-variance-authority';
|
|
209
|
+
|
|
210
|
+
const buttonVariants = cva(
|
|
211
|
+
'inline-flex items-center justify-center rounded-md font-medium transition-colors',
|
|
212
|
+
{
|
|
213
|
+
variants: {
|
|
214
|
+
variant: {
|
|
215
|
+
primary: 'bg-blue-600 text-white hover:bg-blue-700',
|
|
216
|
+
secondary: 'bg-gray-100 text-gray-900 hover:bg-gray-200',
|
|
217
|
+
outline: 'border border-gray-300 hover:bg-gray-50',
|
|
218
|
+
},
|
|
219
|
+
size: {
|
|
220
|
+
sm: 'h-8 px-3 text-sm',
|
|
221
|
+
md: 'h-10 px-4',
|
|
222
|
+
lg: 'h-12 px-6 text-lg',
|
|
223
|
+
},
|
|
224
|
+
},
|
|
225
|
+
defaultVariants: {
|
|
226
|
+
variant: 'primary',
|
|
227
|
+
size: 'md',
|
|
228
|
+
},
|
|
229
|
+
}
|
|
230
|
+
);
|
|
231
|
+
|
|
232
|
+
interface ButtonProps extends VariantProps<typeof buttonVariants> {
|
|
233
|
+
children: React.ReactNode;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function Button({ variant, size, children }: ButtonProps) {
|
|
237
|
+
return <button className={buttonVariants({ variant, size })}>{children}</button>;
|
|
238
|
+
}
|
|
239
|
+
\`\`\`
|
|
240
|
+
|
|
241
|
+
## Tailwind Merge (tw-merge)
|
|
242
|
+
\`\`\`tsx
|
|
243
|
+
import { twMerge } from 'tailwind-merge';
|
|
244
|
+
|
|
245
|
+
// Merge conflicting classes (last wins)
|
|
246
|
+
twMerge('px-2 py-1', 'p-4'); // 'p-4'
|
|
247
|
+
twMerge('text-red-500', 'text-blue-500'); // 'text-blue-500'
|
|
248
|
+
|
|
249
|
+
// With clsx for conditionals
|
|
250
|
+
import clsx from 'clsx';
|
|
251
|
+
|
|
252
|
+
function Button({ className, disabled }) {
|
|
253
|
+
return (
|
|
254
|
+
<button className={twMerge(
|
|
255
|
+
clsx(
|
|
256
|
+
'px-4 py-2 rounded-md',
|
|
257
|
+
disabled && 'opacity-50 cursor-not-allowed',
|
|
258
|
+
),
|
|
259
|
+
className // Allow override from parent
|
|
260
|
+
)} />
|
|
261
|
+
);
|
|
262
|
+
}
|
|
263
|
+
\`\`\`
|
|
264
|
+
|
|
265
|
+
## ❌ DON'T
|
|
266
|
+
- Use @apply excessively (defeats purpose of utility classes)
|
|
267
|
+
- Forget mobile-first approach (sm: is NOT mobile)
|
|
268
|
+
- Create deeply nested arbitrary values
|
|
269
|
+
- Ignore accessibility (focus states, color contrast)
|
|
270
|
+
- Mix Tailwind with large custom CSS files
|
|
271
|
+
|
|
272
|
+
## ✅ DO
|
|
273
|
+
- Use consistent spacing scale (4, 8, 12, 16, 24, 32...)
|
|
274
|
+
- Extract repeated patterns into components
|
|
275
|
+
- Configure theme colors for brand consistency
|
|
276
|
+
- Use CVA or similar for component variants
|
|
277
|
+
- Use tw-merge when accepting className props
|
|
278
|
+
- Prefer built-in colors over arbitrary values
|
|
279
|
+
- Use prose class from @tailwindcss/typography for content
|
|
@@ -0,0 +1,394 @@
|
|
|
1
|
+
# Terraform Skill
|
|
2
|
+
|
|
3
|
+
## Project Structure
|
|
4
|
+
\`\`\`
|
|
5
|
+
infrastructure/
|
|
6
|
+
├── main.tf # Main resources
|
|
7
|
+
├── variables.tf # Input variables
|
|
8
|
+
├── outputs.tf # Output values
|
|
9
|
+
├── providers.tf # Provider config
|
|
10
|
+
├── terraform.tfvars # Variable values (gitignored)
|
|
11
|
+
├── versions.tf # Version constraints
|
|
12
|
+
└── modules/
|
|
13
|
+
└── vpc/
|
|
14
|
+
├── main.tf
|
|
15
|
+
├── variables.tf
|
|
16
|
+
└── outputs.tf
|
|
17
|
+
\`\`\`
|
|
18
|
+
|
|
19
|
+
## Provider Configuration
|
|
20
|
+
\`\`\`hcl
|
|
21
|
+
# versions.tf
|
|
22
|
+
terraform {
|
|
23
|
+
required_version = ">= 1.6.0"
|
|
24
|
+
|
|
25
|
+
required_providers {
|
|
26
|
+
aws = {
|
|
27
|
+
source = "hashicorp/aws"
|
|
28
|
+
version = "~> 5.0"
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
backend "s3" {
|
|
33
|
+
bucket = "my-terraform-state"
|
|
34
|
+
key = "prod/terraform.tfstate"
|
|
35
|
+
region = "us-east-1"
|
|
36
|
+
dynamodb_table = "terraform-locks"
|
|
37
|
+
encrypt = true
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
# providers.tf
|
|
42
|
+
provider "aws" {
|
|
43
|
+
region = var.aws_region
|
|
44
|
+
|
|
45
|
+
default_tags {
|
|
46
|
+
tags = {
|
|
47
|
+
Environment = var.environment
|
|
48
|
+
ManagedBy = "Terraform"
|
|
49
|
+
Project = var.project_name
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
\`\`\`
|
|
54
|
+
|
|
55
|
+
## Variables
|
|
56
|
+
\`\`\`hcl
|
|
57
|
+
# variables.tf
|
|
58
|
+
variable "aws_region" {
|
|
59
|
+
description = "AWS region for resources"
|
|
60
|
+
type = string
|
|
61
|
+
default = "us-east-1"
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
variable "environment" {
|
|
65
|
+
description = "Environment name"
|
|
66
|
+
type = string
|
|
67
|
+
validation {
|
|
68
|
+
condition = contains(["dev", "staging", "prod"], var.environment)
|
|
69
|
+
error_message = "Environment must be dev, staging, or prod."
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
variable "instance_config" {
|
|
74
|
+
description = "EC2 instance configuration"
|
|
75
|
+
type = object({
|
|
76
|
+
instance_type = string
|
|
77
|
+
ami_id = string
|
|
78
|
+
volume_size = number
|
|
79
|
+
})
|
|
80
|
+
default = {
|
|
81
|
+
instance_type = "t3.micro"
|
|
82
|
+
ami_id = "ami-0abcdef1234567890"
|
|
83
|
+
volume_size = 20
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
variable "allowed_cidrs" {
|
|
88
|
+
description = "List of allowed CIDR blocks"
|
|
89
|
+
type = list(string)
|
|
90
|
+
default = []
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
variable "tags" {
|
|
94
|
+
description = "Additional tags"
|
|
95
|
+
type = map(string)
|
|
96
|
+
default = {}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
# terraform.tfvars
|
|
100
|
+
aws_region = "us-west-2"
|
|
101
|
+
environment = "prod"
|
|
102
|
+
\`\`\`
|
|
103
|
+
|
|
104
|
+
## Complete AWS Infrastructure
|
|
105
|
+
\`\`\`hcl
|
|
106
|
+
# main.tf
|
|
107
|
+
|
|
108
|
+
# VPC
|
|
109
|
+
resource "aws_vpc" "main" {
|
|
110
|
+
cidr_block = "10.0.0.0/16"
|
|
111
|
+
enable_dns_hostnames = true
|
|
112
|
+
enable_dns_support = true
|
|
113
|
+
|
|
114
|
+
tags = {
|
|
115
|
+
Name = "\${var.project_name}-vpc"
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
resource "aws_subnet" "public" {
|
|
120
|
+
count = 2
|
|
121
|
+
vpc_id = aws_vpc.main.id
|
|
122
|
+
cidr_block = "10.0.\${count.index + 1}.0/24"
|
|
123
|
+
availability_zone = data.aws_availability_zones.available.names[count.index]
|
|
124
|
+
map_public_ip_on_launch = true
|
|
125
|
+
|
|
126
|
+
tags = {
|
|
127
|
+
Name = "\${var.project_name}-public-\${count.index + 1}"
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
resource "aws_internet_gateway" "main" {
|
|
132
|
+
vpc_id = aws_vpc.main.id
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
# Security Group
|
|
136
|
+
resource "aws_security_group" "web" {
|
|
137
|
+
name = "\${var.project_name}-web-sg"
|
|
138
|
+
description = "Security group for web servers"
|
|
139
|
+
vpc_id = aws_vpc.main.id
|
|
140
|
+
|
|
141
|
+
ingress {
|
|
142
|
+
from_port = 80
|
|
143
|
+
to_port = 80
|
|
144
|
+
protocol = "tcp"
|
|
145
|
+
cidr_blocks = ["0.0.0.0/0"]
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
ingress {
|
|
149
|
+
from_port = 443
|
|
150
|
+
to_port = 443
|
|
151
|
+
protocol = "tcp"
|
|
152
|
+
cidr_blocks = ["0.0.0.0/0"]
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
egress {
|
|
156
|
+
from_port = 0
|
|
157
|
+
to_port = 0
|
|
158
|
+
protocol = "-1"
|
|
159
|
+
cidr_blocks = ["0.0.0.0/0"]
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
lifecycle {
|
|
163
|
+
create_before_destroy = true
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
# EC2 Instance
|
|
168
|
+
resource "aws_instance" "web" {
|
|
169
|
+
ami = var.instance_config.ami_id
|
|
170
|
+
instance_type = var.instance_config.instance_type
|
|
171
|
+
subnet_id = aws_subnet.public[0].id
|
|
172
|
+
vpc_security_group_ids = [aws_security_group.web.id]
|
|
173
|
+
|
|
174
|
+
root_block_device {
|
|
175
|
+
volume_size = var.instance_config.volume_size
|
|
176
|
+
volume_type = "gp3"
|
|
177
|
+
encrypted = true
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
user_data = base64encode(templatefile("\${path.module}/user-data.sh", {
|
|
181
|
+
environment = var.environment
|
|
182
|
+
}))
|
|
183
|
+
|
|
184
|
+
tags = merge(var.tags, {
|
|
185
|
+
Name = "\${var.project_name}-web"
|
|
186
|
+
})
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
# RDS Database
|
|
190
|
+
resource "aws_db_instance" "main" {
|
|
191
|
+
identifier = "\${var.project_name}-db"
|
|
192
|
+
engine = "postgres"
|
|
193
|
+
engine_version = "16.1"
|
|
194
|
+
instance_class = "db.t3.micro"
|
|
195
|
+
|
|
196
|
+
allocated_storage = 20
|
|
197
|
+
max_allocated_storage = 100
|
|
198
|
+
storage_encrypted = true
|
|
199
|
+
|
|
200
|
+
db_name = var.db_name
|
|
201
|
+
username = var.db_username
|
|
202
|
+
password = var.db_password
|
|
203
|
+
|
|
204
|
+
vpc_security_group_ids = [aws_security_group.db.id]
|
|
205
|
+
db_subnet_group_name = aws_db_subnet_group.main.name
|
|
206
|
+
|
|
207
|
+
backup_retention_period = 7
|
|
208
|
+
skip_final_snapshot = var.environment != "prod"
|
|
209
|
+
|
|
210
|
+
lifecycle {
|
|
211
|
+
prevent_destroy = true
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
# Data Sources
|
|
216
|
+
data "aws_availability_zones" "available" {
|
|
217
|
+
state = "available"
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
data "aws_caller_identity" "current" {}
|
|
221
|
+
\`\`\`
|
|
222
|
+
|
|
223
|
+
## Outputs
|
|
224
|
+
\`\`\`hcl
|
|
225
|
+
# outputs.tf
|
|
226
|
+
output "vpc_id" {
|
|
227
|
+
description = "VPC ID"
|
|
228
|
+
value = aws_vpc.main.id
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
output "instance_public_ip" {
|
|
232
|
+
description = "Public IP of web instance"
|
|
233
|
+
value = aws_instance.web.public_ip
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
output "db_endpoint" {
|
|
237
|
+
description = "RDS endpoint"
|
|
238
|
+
value = aws_db_instance.main.endpoint
|
|
239
|
+
sensitive = true
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
output "account_id" {
|
|
243
|
+
value = data.aws_caller_identity.current.account_id
|
|
244
|
+
}
|
|
245
|
+
\`\`\`
|
|
246
|
+
|
|
247
|
+
## Modules
|
|
248
|
+
\`\`\`hcl
|
|
249
|
+
# modules/vpc/main.tf
|
|
250
|
+
resource "aws_vpc" "this" {
|
|
251
|
+
cidr_block = var.cidr_block
|
|
252
|
+
enable_dns_hostnames = true
|
|
253
|
+
tags = var.tags
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
# modules/vpc/variables.tf
|
|
257
|
+
variable "cidr_block" {
|
|
258
|
+
type = string
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
variable "tags" {
|
|
262
|
+
type = map(string)
|
|
263
|
+
default = {}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
# modules/vpc/outputs.tf
|
|
267
|
+
output "vpc_id" {
|
|
268
|
+
value = aws_vpc.this.id
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
# Usage in main.tf
|
|
272
|
+
module "vpc" {
|
|
273
|
+
source = "./modules/vpc"
|
|
274
|
+
|
|
275
|
+
cidr_block = "10.0.0.0/16"
|
|
276
|
+
tags = {
|
|
277
|
+
Environment = var.environment
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
# Public module
|
|
282
|
+
module "vpc" {
|
|
283
|
+
source = "terraform-aws-modules/vpc/aws"
|
|
284
|
+
version = "5.4.0"
|
|
285
|
+
|
|
286
|
+
name = "my-vpc"
|
|
287
|
+
cidr = "10.0.0.0/16"
|
|
288
|
+
|
|
289
|
+
azs = ["us-east-1a", "us-east-1b"]
|
|
290
|
+
private_subnets = ["10.0.1.0/24", "10.0.2.0/24"]
|
|
291
|
+
public_subnets = ["10.0.101.0/24", "10.0.102.0/24"]
|
|
292
|
+
|
|
293
|
+
enable_nat_gateway = true
|
|
294
|
+
single_nat_gateway = true
|
|
295
|
+
}
|
|
296
|
+
\`\`\`
|
|
297
|
+
|
|
298
|
+
## Common Patterns
|
|
299
|
+
\`\`\`hcl
|
|
300
|
+
# Conditional creation
|
|
301
|
+
resource "aws_instance" "bastion" {
|
|
302
|
+
count = var.create_bastion ? 1 : 0
|
|
303
|
+
# ...
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
# for_each with map
|
|
307
|
+
resource "aws_iam_user" "users" {
|
|
308
|
+
for_each = toset(var.user_names)
|
|
309
|
+
name = each.value
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
# Dynamic blocks
|
|
313
|
+
resource "aws_security_group" "example" {
|
|
314
|
+
name = "example"
|
|
315
|
+
|
|
316
|
+
dynamic "ingress" {
|
|
317
|
+
for_each = var.ingress_rules
|
|
318
|
+
content {
|
|
319
|
+
from_port = ingress.value.from_port
|
|
320
|
+
to_port = ingress.value.to_port
|
|
321
|
+
protocol = ingress.value.protocol
|
|
322
|
+
cidr_blocks = ingress.value.cidr_blocks
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
# Locals
|
|
328
|
+
locals {
|
|
329
|
+
common_tags = {
|
|
330
|
+
Environment = var.environment
|
|
331
|
+
Project = var.project_name
|
|
332
|
+
Terraform = "true"
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
name_prefix = "\${var.project_name}-\${var.environment}"
|
|
336
|
+
}
|
|
337
|
+
\`\`\`
|
|
338
|
+
|
|
339
|
+
## Commands
|
|
340
|
+
\`\`\`bash
|
|
341
|
+
# Initialize
|
|
342
|
+
terraform init
|
|
343
|
+
terraform init -upgrade # Upgrade providers
|
|
344
|
+
|
|
345
|
+
# Plan
|
|
346
|
+
terraform plan
|
|
347
|
+
terraform plan -out=plan.tfplan
|
|
348
|
+
terraform plan -var="environment=prod"
|
|
349
|
+
|
|
350
|
+
# Apply
|
|
351
|
+
terraform apply
|
|
352
|
+
terraform apply plan.tfplan
|
|
353
|
+
terraform apply -auto-approve # Skip confirmation
|
|
354
|
+
|
|
355
|
+
# Destroy
|
|
356
|
+
terraform destroy
|
|
357
|
+
terraform destroy -target=aws_instance.web
|
|
358
|
+
|
|
359
|
+
# State management
|
|
360
|
+
terraform state list
|
|
361
|
+
terraform state show aws_instance.web
|
|
362
|
+
terraform state mv aws_instance.old aws_instance.new
|
|
363
|
+
terraform state rm aws_instance.web # Remove from state
|
|
364
|
+
|
|
365
|
+
# Import existing resources
|
|
366
|
+
terraform import aws_instance.web i-1234567890abcdef0
|
|
367
|
+
|
|
368
|
+
# Format & Validate
|
|
369
|
+
terraform fmt -recursive
|
|
370
|
+
terraform validate
|
|
371
|
+
|
|
372
|
+
# Workspaces
|
|
373
|
+
terraform workspace list
|
|
374
|
+
terraform workspace new staging
|
|
375
|
+
terraform workspace select prod
|
|
376
|
+
\`\`\`
|
|
377
|
+
|
|
378
|
+
## ❌ DON'T
|
|
379
|
+
- Commit terraform.tfvars with secrets
|
|
380
|
+
- Use \`-auto-approve\` in production without review
|
|
381
|
+
- Store state locally for team projects
|
|
382
|
+
- Hardcode values instead of variables
|
|
383
|
+
- Ignore \`prevent_destroy\` for critical resources
|
|
384
|
+
- Skip \`terraform plan\` before apply
|
|
385
|
+
|
|
386
|
+
## ✅ DO
|
|
387
|
+
- Use remote state (S3, Terraform Cloud)
|
|
388
|
+
- Enable state locking (DynamoDB)
|
|
389
|
+
- Use workspaces or directories for environments
|
|
390
|
+
- Pin provider versions
|
|
391
|
+
- Use modules for reusability
|
|
392
|
+
- Mark sensitive outputs
|
|
393
|
+
- Use lifecycle rules appropriately
|
|
394
|
+
- Validate and format before commit
|