Utilizziamo i cookie per migliorare la tua esperienza sul sito
CodeWorlds
Torna alle collezioni
Guide37 min read

Amazon Q Developer

Amazon Q Developer is a powerful AI assistant from AWS specializing in cloud programming, infrastructure management, and AWS service integration. Complete guide to cloud development tool.

Amazon Q Developer - Kompletny Przewodnik po AI dla AWS

Czym jest Amazon Q Developer?

Amazon Q Developer (dawniej Amazon CodeWhisperer) to zaawansowany asystent AI od Amazon Web Services, zaprojektowany specjalnie dla programistów pracujących z ekosystemem AWS. W przeciwieństwie do innych asystentów AI, Amazon Q ma głęboką wiedzę o wszystkich serwisach AWS i potrafi generować kod, który jest zgodny z best practices AWS, bezpieczny i zoptymalizowany pod kątem kosztów.

Amazon Q to nie tylko autouzupełnianie kodu - to kompleksowe narzędzie do budowania, debugowania, optymalizacji i zabezpieczania aplikacji cloud-native. Integruje się bezpośrednio z AWS Console, IDE i CLI, oferując pomoc na każdym etapie rozwoju aplikacji.

Dlaczego Amazon Q Developer?

Kluczowe zalety Amazon Q

  1. Głęboka wiedza AWS - Zna wszystkie 200+ serwisów AWS
  2. Security scanning - Automatyczne wykrywanie luk bezpieczeństwa
  3. Code transformation - Automatyczna modernizacja kodu (np. Java 8→17)
  4. Infrastructure as Code - Generowanie CloudFormation, CDK, Terraform
  5. Cost optimization - Sugestie optymalizacji kosztów
  6. Bezpłatny tier - Darmowy dostęp dla indywidualnych deweloperów
  7. Compliance - Zgodność z SOC 2, HIPAA, FedRAMP

Amazon Q vs inne AI assistants dla cloud

AspektAmazon QGitHub CopilotGoogle Cloud AIAzure AI
AWS IntegrationNatywnaBrakBrakBrak
Security ScanningWbudowaneOsobny produktOgraniczoneOgraniczone
IaC GenerationCloudFormation, CDKPodstawoweTerraformARM, Bicep
Cost AnalysisTakNieNieNie
Free TierTakNieTakTak
Console IntegrationPełnaBrakPełnaPełna

Instalacja i Konfiguracja

VS Code

Code
Bash
# 1. Otwórz VS Code
# 2. Extensions (Ctrl+Shift+X)
# 3. Szukaj "Amazon Q"
# 4. Zainstaluj "Amazon Q"

# Lub z terminala:
code --install-extension amazonwebservices.amazon-q-vscode

JetBrains IDEs

Code
Bash
# 1. File → Settings → Plugins
# 2. Marketplace → Szukaj "Amazon Q"
# 3. Install → Restart IDE

Visual Studio

Code
Bash
# 1. Extensions → Manage Extensions
# 2. Online → Szukaj "AWS Toolkit"
# 3. Download → Restart VS

AWS CLI Integration

Code
Bash
# Instalacja AWS CLI
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
unzip awscliv2.zip
sudo ./aws/install

# Konfiguracja
aws configure
# AWS Access Key ID: YOUR_ACCESS_KEY
# AWS Secret Access Key: YOUR_SECRET_KEY
# Default region name: eu-central-1
# Default output format: json

# Instalacja Amazon Q CLI
pip install amazon-q-developer-cli

Autoryzacja

Code
Bash
# Opcja 1: AWS Builder ID (darmowe)
# - Kliknij "Sign in" w rozszerzeniu
# - Wybierz "Use personal email"
# - Utwórz AWS Builder ID

# Opcja 2: AWS IAM Identity Center (enterprise)
# - Kliknij "Sign in"
# - Wybierz "Use IAM Identity Center"
# - Wprowadź Start URL organizacji

Generowanie Kodu AWS

Lambda Functions

Code
TypeScript
// Amazon Q rozumie kontekst Lambda i AWS SDK

// Prompt: "Create Lambda function to process S3 events"

import { S3Event, Context } from 'aws-lambda'
import { S3Client, GetObjectCommand, PutObjectCommand } from '@aws-sdk/client-s3'

const s3Client = new S3Client({ region: process.env.AWS_REGION })

export const handler = async (event: S3Event, context: Context) => {
  console.log('Processing S3 event:', JSON.stringify(event, null, 2))

  for (const record of event.Records) {
    const bucket = record.s3.bucket.name
    const key = decodeURIComponent(record.s3.object.key.replace(/\+/g, ' '))

    try {
      // Pobierz obiekt z S3
      const getCommand = new GetObjectCommand({ Bucket: bucket, Key: key })
      const response = await s3Client.send(getCommand)

      // Przetwórz dane
      const body = await response.Body?.transformToString()
      const processedData = processData(body)

      // Zapisz przetworzony plik
      const putCommand = new PutObjectCommand({
        Bucket: bucket,
        Key: `processed/${key}`,
        Body: JSON.stringify(processedData),
        ContentType: 'application/json'
      })
      await s3Client.send(putCommand)

      console.log(`Successfully processed: ${key}`)
    } catch (error) {
      console.error(`Error processing ${key}:`, error)
      throw error
    }
  }

  return {
    statusCode: 200,
    body: JSON.stringify({ message: 'Processing complete' })
  }
}

function processData(data: string | undefined): object {
  if (!data) return {}
  // Amazon Q sugeruje logikę przetwarzania
  return JSON.parse(data)
}

DynamoDB Operations

Code
Python
# Amazon Q zna best practices DynamoDB

# Prompt: "Create DynamoDB CRUD operations with optimistic locking"

import boto3
from boto3.dynamodb.conditions import Key, Attr
from botocore.exceptions import ClientError
from decimal import Decimal
import json

dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table('Users')

def create_user(user_data: dict) -> dict:
    """Create a new user with initial version."""
    user_data['version'] = 1
    user_data['created_at'] = datetime.now().isoformat()
    user_data['updated_at'] = datetime.now().isoformat()

    try:
        table.put_item(
            Item=user_data,
            ConditionExpression='attribute_not_exists(user_id)'
        )
        return {'success': True, 'user': user_data}
    except ClientError as e:
        if e.response['Error']['Code'] == 'ConditionalCheckFailedException':
            return {'success': False, 'error': 'User already exists'}
        raise


def get_user(user_id: str) -> dict:
    """Get user by ID with consistent read."""
    response = table.get_item(
        Key={'user_id': user_id},
        ConsistentRead=True
    )
    return response.get('Item')


def update_user(user_id: str, updates: dict, current_version: int) -> dict:
    """Update user with optimistic locking."""
    try:
        # Build update expression dynamically
        update_expr = 'SET version = version + :inc, updated_at = :updated'
        expr_values = {
            ':inc': 1,
            ':updated': datetime.now().isoformat(),
            ':current_version': current_version
        }
        expr_names = {}

        for key, value in updates.items():
            safe_key = f'#{key}'
            update_expr += f', {safe_key} = :{key}'
            expr_values[f':{key}'] = value
            expr_names[safe_key] = key

        response = table.update_item(
            Key={'user_id': user_id},
            UpdateExpression=update_expr,
            ExpressionAttributeValues=expr_values,
            ExpressionAttributeNames=expr_names if expr_names else None,
            ConditionExpression='version = :current_version',
            ReturnValues='ALL_NEW'
        )
        return {'success': True, 'user': response['Attributes']}

    except ClientError as e:
        if e.response['Error']['Code'] == 'ConditionalCheckFailedException':
            return {'success': False, 'error': 'Version conflict - retry required'}
        raise


def query_users_by_email_domain(domain: str) -> list:
    """Query users by email domain using GSI."""
    response = table.query(
        IndexName='email-domain-index',
        KeyConditionExpression=Key('email_domain').eq(domain),
        FilterExpression=Attr('active').eq(True)
    )
    return response['Items']


def batch_get_users(user_ids: list) -> list:
    """Batch get multiple users efficiently."""
    # DynamoDB limit: 100 items per batch
    results = []
    for i in range(0, len(user_ids), 100):
        batch = user_ids[i:i+100]
        response = dynamodb.batch_get_item(
            RequestItems={
                'Users': {
                    'Keys': [{'user_id': uid} for uid in batch],
                    'ConsistentRead': True
                }
            }
        )
        results.extend(response['Responses']['Users'])

        # Handle unprocessed keys
        while response.get('UnprocessedKeys'):
            response = dynamodb.batch_get_item(
                RequestItems=response['UnprocessedKeys']
            )
            results.extend(response['Responses']['Users'])

    return results

API Gateway + Lambda

Code
TypeScript
// Amazon Q generuje kompletne API z walidacją

// Prompt: "Create REST API with API Gateway and Lambda"

import {
  APIGatewayProxyEvent,
  APIGatewayProxyResult,
  Context
} from 'aws-lambda'
import { DynamoDBClient } from '@aws-sdk/client-dynamodb'
import {
  DynamoDBDocumentClient,
  GetCommand,
  PutCommand,
  UpdateCommand,
  DeleteCommand,
  QueryCommand
} from '@aws-sdk/lib-dynamodb'

const client = new DynamoDBClient({})
const docClient = DynamoDBDocumentClient.from(client)
const TABLE_NAME = process.env.TABLE_NAME!

// Middleware dla CORS i error handling
const withMiddleware = (
  handler: (event: APIGatewayProxyEvent) => Promise<any>
) => {
  return async (
    event: APIGatewayProxyEvent,
    context: Context
  ): Promise<APIGatewayProxyResult> => {
    const headers = {
      'Content-Type': 'application/json',
      'Access-Control-Allow-Origin': '*',
      'Access-Control-Allow-Headers': 'Content-Type,Authorization',
      'Access-Control-Allow-Methods': 'GET,POST,PUT,DELETE,OPTIONS'
    }

    // Handle preflight
    if (event.httpMethod === 'OPTIONS') {
      return { statusCode: 200, headers, body: '' }
    }

    try {
      const result = await handler(event)
      return {
        statusCode: result.statusCode || 200,
        headers,
        body: JSON.stringify(result.body)
      }
    } catch (error: any) {
      console.error('Error:', error)
      return {
        statusCode: error.statusCode || 500,
        headers,
        body: JSON.stringify({
          error: error.message || 'Internal Server Error'
        })
      }
    }
  }
}

// GET /users/{id}
export const getUser = withMiddleware(async (event) => {
  const userId = event.pathParameters?.id

  if (!userId) {
    throw { statusCode: 400, message: 'User ID is required' }
  }

  const command = new GetCommand({
    TableName: TABLE_NAME,
    Key: { pk: `USER#${userId}`, sk: `USER#${userId}` }
  })

  const result = await docClient.send(command)

  if (!result.Item) {
    throw { statusCode: 404, message: 'User not found' }
  }

  return { body: result.Item }
})

// POST /users
export const createUser = withMiddleware(async (event) => {
  const body = JSON.parse(event.body || '{}')

  // Walidacja
  if (!body.email || !body.name) {
    throw { statusCode: 400, message: 'Email and name are required' }
  }

  const userId = generateId()
  const timestamp = new Date().toISOString()

  const user = {
    pk: `USER#${userId}`,
    sk: `USER#${userId}`,
    id: userId,
    email: body.email,
    name: body.name,
    createdAt: timestamp,
    updatedAt: timestamp,
    GSI1PK: `EMAIL#${body.email}`,
    GSI1SK: `USER#${userId}`
  }

  const command = new PutCommand({
    TableName: TABLE_NAME,
    Item: user,
    ConditionExpression: 'attribute_not_exists(pk)'
  })

  await docClient.send(command)

  return { statusCode: 201, body: user }
})

// PUT /users/{id}
export const updateUser = withMiddleware(async (event) => {
  const userId = event.pathParameters?.id
  const body = JSON.parse(event.body || '{}')

  if (!userId) {
    throw { statusCode: 400, message: 'User ID is required' }
  }

  const updateExpressions: string[] = []
  const expressionValues: Record<string, any> = {}
  const expressionNames: Record<string, string> = {}

  if (body.name) {
    updateExpressions.push('#name = :name')
    expressionValues[':name'] = body.name
    expressionNames['#name'] = 'name'
  }

  if (body.email) {
    updateExpressions.push('email = :email')
    expressionValues[':email'] = body.email
  }

  updateExpressions.push('updatedAt = :updatedAt')
  expressionValues[':updatedAt'] = new Date().toISOString()

  const command = new UpdateCommand({
    TableName: TABLE_NAME,
    Key: { pk: `USER#${userId}`, sk: `USER#${userId}` },
    UpdateExpression: `SET ${updateExpressions.join(', ')}`,
    ExpressionAttributeValues: expressionValues,
    ExpressionAttributeNames: Object.keys(expressionNames).length > 0
      ? expressionNames
      : undefined,
    ReturnValues: 'ALL_NEW',
    ConditionExpression: 'attribute_exists(pk)'
  })

  const result = await docClient.send(command)
  return { body: result.Attributes }
})

// DELETE /users/{id}
export const deleteUser = withMiddleware(async (event) => {
  const userId = event.pathParameters?.id

  if (!userId) {
    throw { statusCode: 400, message: 'User ID is required' }
  }

  const command = new DeleteCommand({
    TableName: TABLE_NAME,
    Key: { pk: `USER#${userId}`, sk: `USER#${userId}` },
    ConditionExpression: 'attribute_exists(pk)'
  })

  await docClient.send(command)
  return { statusCode: 204, body: null }
})

function generateId(): string {
  return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`
}

Infrastructure as Code

CloudFormation

Code
YAML
# Amazon Q generuje kompletne szablony CloudFormation

# Prompt: "Create CloudFormation template for serverless API"

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: Serverless API with Lambda, API Gateway, and DynamoDB

Parameters:
  Environment:
    Type: String
    Default: dev
    AllowedValues:
      - dev
      - staging
      - prod

  TableReadCapacity:
    Type: Number
    Default: 5

  TableWriteCapacity:
    Type: Number
    Default: 5

Globals:
  Function:
    Runtime: nodejs18.x
    Timeout: 30
    MemorySize: 256
    Environment:
      Variables:
        TABLE_NAME: !Ref UsersTable
        ENVIRONMENT: !Ref Environment
    Tracing: Active

Resources:
  # DynamoDB Table
  UsersTable:
    Type: AWS::DynamoDB::Table
    Properties:
      TableName: !Sub ${AWS::StackName}-users
      BillingMode: PROVISIONED
      ProvisionedThroughput:
        ReadCapacityUnits: !Ref TableReadCapacity
        WriteCapacityUnits: !Ref TableWriteCapacity
      AttributeDefinitions:
        - AttributeName: pk
          AttributeType: S
        - AttributeName: sk
          AttributeType: S
        - AttributeName: GSI1PK
          AttributeType: S
        - AttributeName: GSI1SK
          AttributeType: S
      KeySchema:
        - AttributeName: pk
          KeyType: HASH
        - AttributeName: sk
          KeyType: RANGE
      GlobalSecondaryIndexes:
        - IndexName: GSI1
          KeySchema:
            - AttributeName: GSI1PK
              KeyType: HASH
            - AttributeName: GSI1SK
              KeyType: RANGE
          Projection:
            ProjectionType: ALL
          ProvisionedThroughput:
            ReadCapacityUnits: !Ref TableReadCapacity
            WriteCapacityUnits: !Ref TableWriteCapacity
      PointInTimeRecoverySpecification:
        PointInTimeRecoveryEnabled: true
      SSESpecification:
        SSEEnabled: true

  # API Gateway
  ApiGateway:
    Type: AWS::Serverless::Api
    Properties:
      Name: !Sub ${AWS::StackName}-api
      StageName: !Ref Environment
      Cors:
        AllowMethods: "'GET,POST,PUT,DELETE,OPTIONS'"
        AllowHeaders: "'Content-Type,Authorization'"
        AllowOrigin: "'*'"
      Auth:
        DefaultAuthorizer: CognitoAuthorizer
        Authorizers:
          CognitoAuthorizer:
            UserPoolArn: !GetAtt UserPool.Arn

  # Lambda Functions
  GetUserFunction:
    Type: AWS::Serverless::Function
    Properties:
      FunctionName: !Sub ${AWS::StackName}-get-user
      Handler: dist/handlers/users.getUser
      CodeUri: ./
      Policies:
        - DynamoDBReadPolicy:
            TableName: !Ref UsersTable
      Events:
        Api:
          Type: Api
          Properties:
            RestApiId: !Ref ApiGateway
            Path: /users/{id}
            Method: GET

  CreateUserFunction:
    Type: AWS::Serverless::Function
    Properties:
      FunctionName: !Sub ${AWS::StackName}-create-user
      Handler: dist/handlers/users.createUser
      CodeUri: ./
      Policies:
        - DynamoDBCrudPolicy:
            TableName: !Ref UsersTable
      Events:
        Api:
          Type: Api
          Properties:
            RestApiId: !Ref ApiGateway
            Path: /users
            Method: POST

  UpdateUserFunction:
    Type: AWS::Serverless::Function
    Properties:
      FunctionName: !Sub ${AWS::StackName}-update-user
      Handler: dist/handlers/users.updateUser
      CodeUri: ./
      Policies:
        - DynamoDBCrudPolicy:
            TableName: !Ref UsersTable
      Events:
        Api:
          Type: Api
          Properties:
            RestApiId: !Ref ApiGateway
            Path: /users/{id}
            Method: PUT

  DeleteUserFunction:
    Type: AWS::Serverless::Function
    Properties:
      FunctionName: !Sub ${AWS::StackName}-delete-user
      Handler: dist/handlers/users.deleteUser
      CodeUri: ./
      Policies:
        - DynamoDBCrudPolicy:
            TableName: !Ref UsersTable
      Events:
        Api:
          Type: Api
          Properties:
            RestApiId: !Ref ApiGateway
            Path: /users/{id}
            Method: DELETE

  # Cognito User Pool
  UserPool:
    Type: AWS::Cognito::UserPool
    Properties:
      UserPoolName: !Sub ${AWS::StackName}-users
      AutoVerifiedAttributes:
        - email
      UsernameAttributes:
        - email
      Policies:
        PasswordPolicy:
          MinimumLength: 8
          RequireLowercase: true
          RequireNumbers: true
          RequireSymbols: true
          RequireUppercase: true

  UserPoolClient:
    Type: AWS::Cognito::UserPoolClient
    Properties:
      ClientName: !Sub ${AWS::StackName}-client
      UserPoolId: !Ref UserPool
      GenerateSecret: false
      ExplicitAuthFlows:
        - ALLOW_USER_PASSWORD_AUTH
        - ALLOW_REFRESH_TOKEN_AUTH

Outputs:
  ApiUrl:
    Description: API Gateway URL
    Value: !Sub https://${ApiGateway}.execute-api.${AWS::Region}.amazonaws.com/${Environment}
    Export:
      Name: !Sub ${AWS::StackName}-api-url

  TableName:
    Description: DynamoDB Table Name
    Value: !Ref UsersTable
    Export:
      Name: !Sub ${AWS::StackName}-table-name

  UserPoolId:
    Description: Cognito User Pool ID
    Value: !Ref UserPool
    Export:
      Name: !Sub ${AWS::StackName}-user-pool-id

  UserPoolClientId:
    Description: Cognito User Pool Client ID
    Value: !Ref UserPoolClient
    Export:
      Name: !Sub ${AWS::StackName}-user-pool-client-id

AWS CDK

Code
TypeScript
// Amazon Q generuje kompletne staki CDK

// Prompt: "Create CDK stack for ECS Fargate service"

import * as cdk from 'aws-cdk-lib'
import { Construct } from 'constructs'
import * as ec2 from 'aws-cdk-lib/aws-ec2'
import * as ecs from 'aws-cdk-lib/aws-ecs'
import * as ecr from 'aws-cdk-lib/aws-ecr'
import * as elbv2 from 'aws-cdk-lib/aws-elasticloadbalancingv2'
import * as logs from 'aws-cdk-lib/aws-logs'
import * as secretsmanager from 'aws-cdk-lib/aws-secretsmanager'
import * as rds from 'aws-cdk-lib/aws-rds'

interface ApiStackProps extends cdk.StackProps {
  environment: 'dev' | 'staging' | 'prod'
  domainName?: string
}

export class ApiStack extends cdk.Stack {
  public readonly loadBalancer: elbv2.ApplicationLoadBalancer
  public readonly service: ecs.FargateService

  constructor(scope: Construct, id: string, props: ApiStackProps) {
    super(scope, id, props)

    // VPC
    const vpc = new ec2.Vpc(this, 'Vpc', {
      maxAzs: 3,
      natGateways: props.environment === 'prod' ? 3 : 1,
      subnetConfiguration: [
        {
          name: 'Public',
          subnetType: ec2.SubnetType.PUBLIC,
          cidrMask: 24
        },
        {
          name: 'Private',
          subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS,
          cidrMask: 24
        },
        {
          name: 'Isolated',
          subnetType: ec2.SubnetType.PRIVATE_ISOLATED,
          cidrMask: 24
        }
      ]
    })

    // Security Groups
    const albSecurityGroup = new ec2.SecurityGroup(this, 'AlbSg', {
      vpc,
      description: 'ALB Security Group',
      allowAllOutbound: true
    })
    albSecurityGroup.addIngressRule(
      ec2.Peer.anyIpv4(),
      ec2.Port.tcp(443),
      'HTTPS'
    )
    albSecurityGroup.addIngressRule(
      ec2.Peer.anyIpv4(),
      ec2.Port.tcp(80),
      'HTTP redirect'
    )

    const ecsSecurityGroup = new ec2.SecurityGroup(this, 'EcsSg', {
      vpc,
      description: 'ECS Security Group',
      allowAllOutbound: true
    })
    ecsSecurityGroup.addIngressRule(
      albSecurityGroup,
      ec2.Port.tcp(3000),
      'From ALB'
    )

    const dbSecurityGroup = new ec2.SecurityGroup(this, 'DbSg', {
      vpc,
      description: 'RDS Security Group'
    })
    dbSecurityGroup.addIngressRule(
      ecsSecurityGroup,
      ec2.Port.tcp(5432),
      'From ECS'
    )

    // RDS PostgreSQL
    const dbSecret = new secretsmanager.Secret(this, 'DbSecret', {
      secretName: `${id}/database`,
      generateSecretString: {
        secretStringTemplate: JSON.stringify({ username: 'admin' }),
        generateStringKey: 'password',
        excludePunctuation: true
      }
    })

    const database = new rds.DatabaseInstance(this, 'Database', {
      engine: rds.DatabaseInstanceEngine.postgres({
        version: rds.PostgresEngineVersion.VER_15
      }),
      instanceType: ec2.InstanceType.of(
        ec2.InstanceClass.T3,
        props.environment === 'prod'
          ? ec2.InstanceSize.MEDIUM
          : ec2.InstanceSize.MICRO
      ),
      vpc,
      vpcSubnets: { subnetType: ec2.SubnetType.PRIVATE_ISOLATED },
      securityGroups: [dbSecurityGroup],
      credentials: rds.Credentials.fromSecret(dbSecret),
      multiAz: props.environment === 'prod',
      allocatedStorage: 20,
      maxAllocatedStorage: 100,
      deletionProtection: props.environment === 'prod',
      removalPolicy: props.environment === 'prod'
        ? cdk.RemovalPolicy.RETAIN
        : cdk.RemovalPolicy.DESTROY
    })

    // ECS Cluster
    const cluster = new ecs.Cluster(this, 'Cluster', {
      vpc,
      containerInsights: true,
      enableFargateCapacityProviders: true
    })

    // ECR Repository
    const repository = new ecr.Repository(this, 'Repository', {
      repositoryName: `${id.toLowerCase()}-api`,
      removalPolicy: cdk.RemovalPolicy.DESTROY,
      imageScanOnPush: true,
      lifecycleRules: [
        {
          maxImageCount: 10,
          rulePriority: 1
        }
      ]
    })

    // Task Definition
    const taskDefinition = new ecs.FargateTaskDefinition(this, 'TaskDef', {
      memoryLimitMiB: props.environment === 'prod' ? 2048 : 512,
      cpu: props.environment === 'prod' ? 1024 : 256
    })

    const container = taskDefinition.addContainer('api', {
      image: ecs.ContainerImage.fromEcrRepository(repository, 'latest'),
      logging: ecs.LogDrivers.awsLogs({
        streamPrefix: 'api',
        logRetention: logs.RetentionDays.ONE_MONTH
      }),
      environment: {
        NODE_ENV: props.environment,
        PORT: '3000'
      },
      secrets: {
        DATABASE_URL: ecs.Secret.fromSecretsManager(database.secret!),
        DB_HOST: ecs.Secret.fromSecretsManager(database.secret!, 'host'),
        DB_PASSWORD: ecs.Secret.fromSecretsManager(database.secret!, 'password')
      },
      healthCheck: {
        command: ['CMD-SHELL', 'curl -f http://localhost:3000/health || exit 1'],
        interval: cdk.Duration.seconds(30),
        timeout: cdk.Duration.seconds(5),
        retries: 3
      }
    })

    container.addPortMappings({ containerPort: 3000 })

    // Fargate Service
    this.service = new ecs.FargateService(this, 'Service', {
      cluster,
      taskDefinition,
      desiredCount: props.environment === 'prod' ? 3 : 1,
      securityGroups: [ecsSecurityGroup],
      vpcSubnets: { subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS },
      capacityProviderStrategies: [
        {
          capacityProvider: 'FARGATE_SPOT',
          weight: props.environment === 'prod' ? 1 : 2
        },
        {
          capacityProvider: 'FARGATE',
          weight: props.environment === 'prod' ? 2 : 1
        }
      ],
      circuitBreaker: { rollback: true },
      enableExecuteCommand: true
    })

    // Auto Scaling
    const scaling = this.service.autoScaleTaskCount({
      minCapacity: props.environment === 'prod' ? 3 : 1,
      maxCapacity: props.environment === 'prod' ? 20 : 5
    })

    scaling.scaleOnCpuUtilization('CpuScaling', {
      targetUtilizationPercent: 70,
      scaleInCooldown: cdk.Duration.seconds(60),
      scaleOutCooldown: cdk.Duration.seconds(60)
    })

    scaling.scaleOnMemoryUtilization('MemoryScaling', {
      targetUtilizationPercent: 70
    })

    // Application Load Balancer
    this.loadBalancer = new elbv2.ApplicationLoadBalancer(this, 'Alb', {
      vpc,
      internetFacing: true,
      securityGroup: albSecurityGroup
    })

    const listener = this.loadBalancer.addListener('HttpsListener', {
      port: 443,
      certificates: props.domainName
        ? [/* Add certificate */]
        : undefined,
      protocol: props.domainName
        ? elbv2.ApplicationProtocol.HTTPS
        : elbv2.ApplicationProtocol.HTTP
    })

    listener.addTargets('EcsTarget', {
      port: 3000,
      targets: [this.service],
      healthCheck: {
        path: '/health',
        interval: cdk.Duration.seconds(30),
        healthyThresholdCount: 2,
        unhealthyThresholdCount: 3
      },
      deregistrationDelay: cdk.Duration.seconds(30)
    })

    // HTTP to HTTPS redirect
    if (props.domainName) {
      this.loadBalancer.addListener('HttpListener', {
        port: 80,
        defaultAction: elbv2.ListenerAction.redirect({
          port: '443',
          protocol: 'HTTPS',
          permanent: true
        })
      })
    }

    // Outputs
    new cdk.CfnOutput(this, 'LoadBalancerDns', {
      value: this.loadBalancer.loadBalancerDnsName
    })

    new cdk.CfnOutput(this, 'RepositoryUri', {
      value: repository.repositoryUri
    })
  }
}

Security Scanning

Automatyczne skanowanie kodu

Code
TypeScript
// Amazon Q automatycznie wykrywa problemy bezpieczeństwa

// ⚠️ Amazon Q ostrzega:
// SQL Injection vulnerability detected
const query = `SELECT * FROM users WHERE id = '${userId}'`

// ✅ Amazon Q sugeruje poprawkę:
import { DynamoDBDocumentClient, GetCommand } from '@aws-sdk/lib-dynamodb'

const command = new GetCommand({
  TableName: 'users',
  Key: { id: userId }
})
const result = await docClient.send(command)

Typy wykrywanych problemów

Code
TEXT
┌─────────────────────────────────────────────────────────┐
│           AMAZON Q SECURITY SCANNING                     │
├─────────────────────────────────────────────────────────┤
│                                                         │
│   ⚠️ Injection Vulnerabilities                          │
│      └─ SQL, NoSQL, Command, LDAP injection            │
│                                                         │
│   ⚠️ Authentication Issues                              │
│      └─ Hardcoded credentials, weak passwords          │
│                                                         │
│   ⚠️ Sensitive Data Exposure                            │
│      └─ API keys, secrets in code                      │
│                                                         │
│   ⚠️ Security Misconfiguration                          │
│      └─ Open S3 buckets, public resources             │
│                                                         │
│   ⚠️ Cross-Site Scripting (XSS)                         │
│      └─ Unescaped user input                           │
│                                                         │
│   ⚠️ Insecure Dependencies                              │
│      └─ Known CVEs in packages                         │
│                                                         │
└─────────────────────────────────────────────────────────┘

Skanowanie w CI/CD

.github/workflows/security-scan.yml
YAML
# .github/workflows/security-scan.yml
name: Security Scan

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

jobs:
  amazon-q-scan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Configure AWS Credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: eu-central-1

      - name: Run Amazon Q Security Scan
        run: |
          pip install amazon-q-developer-cli
          amazon-q scan --format sarif --output results.sarif

      - name: Upload SARIF results
        uses: github/codeql-action/upload-sarif@v2
        with:
          sarif_file: results.sarif

      - name: Fail on high severity
        run: |
          HIGH_COUNT=$(jq '.runs[0].results | map(select(.level == "error")) | length' results.sarif)
          if [ "$HIGH_COUNT" -gt 0 ]; then
            echo "Found $HIGH_COUNT high severity issues"
            exit 1
          fi

Code Transformation

Java Modernization

Code
Java
// Amazon Q może automatycznie zmodernizować kod Java

// PRZED (Java 8):
public class UserService {
    private List<User> users = new ArrayList<>();

    public List<User> findActiveUsers() {
        List<User> result = new ArrayList<>();
        for (User user : users) {
            if (user.isActive()) {
                result.add(user);
            }
        }
        return result;
    }

    public Optional<User> findByEmail(String email) {
        for (User user : users) {
            if (user.getEmail().equals(email)) {
                return Optional.of(user);
            }
        }
        return Optional.empty();
    }
}

// PO (Java 17) - Amazon Q transformation:
public class UserService {
    private final List<User> users = new ArrayList<>();

    public List<User> findActiveUsers() {
        return users.stream()
            .filter(User::isActive)
            .toList();
    }

    public Optional<User> findByEmail(String email) {
        return users.stream()
            .filter(user -> user.getEmail().equals(email))
            .findFirst();
    }
}

.NET Modernization

Code
C#
// Amazon Q modernizuje .NET Framework do .NET 8

// PRZED (.NET Framework 4.8):
public class DataService
{
    public async Task<List<Customer>> GetCustomersAsync()
    {
        using (var client = new HttpClient())
        {
            var response = await client.GetAsync("https://api.example.com/customers");
            var content = await response.Content.ReadAsStringAsync();
            return JsonConvert.DeserializeObject<List<Customer>>(content);
        }
    }
}

// PO (.NET 8) - Amazon Q transformation:
public class DataService(HttpClient httpClient)
{
    public async Task<List<Customer>> GetCustomersAsync()
    {
        return await httpClient.GetFromJsonAsync<List<Customer>>(
            "https://api.example.com/customers"
        ) ?? [];
    }
}

Amazon Q w AWS Console

Chat w Console

Code
TEXT
User: How do I set up a VPC with public and private subnets?

Amazon Q:
I'll help you create a VPC with public and private subnets. Here's what you need:

1. **VPC**: Create a VPC with CIDR block (e.g., 10.0.0.0/16)

2. **Subnets**:
   - Public: 10.0.1.0/24 (for ALB, NAT Gateway)
   - Private: 10.0.2.0/24 (for EC2, Lambda)

3. **Internet Gateway**: Attach to VPC for public internet access

4. **NAT Gateway**: Place in public subnet for private subnet outbound access

5. **Route Tables**:
   - Public: Route 0.0.0.0/0 → Internet Gateway
   - Private: Route 0.0.0.0/0 → NAT Gateway

Would you like me to create a CloudFormation template for this setup?

Troubleshooting z Amazon Q

Code
TEXT
User: My Lambda function is timing out when connecting to RDS

Amazon Q:
Based on the symptoms, here are the most common causes and solutions:

**1. VPC Configuration Issue (Most Common)**
- Lambda needs to be in the same VPC as RDS
- Check Security Groups allow traffic on port 5432/3306
- Ensure Lambda has a route to RDS subnet

**2. Security Group Configuration**

RDS Security Group should allow:

Inbound: Port 5432 from Lambda Security Group

Code
TEXT
**3. NAT Gateway for Internet Access**
If Lambda needs both RDS and internet access, ensure:
- Lambda is in private subnet
- NAT Gateway is in public subnet
- Route table has 0.0.0.0/0 → NAT Gateway

**4. Connection Pool Exhaustion**
```python
# Use connection pooling
from sqlalchemy import create_engine
engine = create_engine(DATABASE_URL, pool_size=5, max_overflow=10)

5. Increase Timeout

Code
YAML
# SAM template
Timeout: 30  # Increase from default 3 seconds

Would you like me to help diagnose which specific issue you're facing?

Code
TEXT
## Cennik

### Free Tier

**Cena**: $0/miesiąc

**Zawiera:**
- 50 security scans/miesiąc
- Podstawowe code suggestions
- Integracja z VS Code, JetBrains
- Dostęp do Amazon Q Chat
- AWS Console integration

### Pro Tier

**Cena**: $19/użytkownika/miesiąc

**Zawiera:**
- Wszystko z Free
- Nieograniczone security scans
- Code transformation
- Administrative controls
- Higher quotas
- Priority support

### Enterprise

**Cena**: Custom

**Zawiera:**
- Wszystko z Pro
- SSO/SAML
- Customization options
- Dedicated support
- SLA guarantees
- Compliance reporting

## Best Practices

### Efektywne prompty

```typescript
// ❌ Zbyt ogólne
// "Create Lambda function"

// ✅ Szczegółowe
// "Create Lambda function that:
//  - Processes S3 events for image uploads
//  - Resizes images to 800x600
//  - Stores thumbnails in a separate bucket
//  - Uses Sharp library for image processing
//  - Includes error handling and CloudWatch logging"

Konfiguracja projektu

.amazonq/config.json
JSON
// .amazonq/config.json
{
  "codeGeneration": {
    "preferredLanguage": "typescript",
    "preferredRuntime": "nodejs18.x",
    "preferredRegion": "eu-central-1"
  },
  "securityScanning": {
    "enabled": true,
    "severity": "HIGH",
    "autoFix": false
  },
  "infraAsCode": {
    "preferredTool": "cdk",
    "language": "typescript"
  }
}

FAQ

Czy Amazon Q jest darmowy?

Tak, Amazon Q ma darmowy tier z 50 security scans miesięcznie i podstawowymi sugestiami kodu. Pro tier kosztuje $19/użytkownika/miesiąc.

Czy Amazon Q działa tylko z AWS?

Amazon Q jest zoptymalizowany dla AWS, ale może generować ogólny kod w wielu językach. Jednak jego największa wartość to głęboka integracja z ekosystemem AWS.

Jak Amazon Q różni się od Copilot?

Amazon Q specjalizuje się w AWS - zna wszystkie serwisy, best practices, security patterns. Copilot jest bardziej uniwersalny, ale nie ma tak głębokiej wiedzy o AWS.

Czy mogę używać Amazon Q offline?

Nie, Amazon Q wymaga połączenia z internetem do generowania sugestii i skanowania bezpieczeństwa.

Podsumowanie

Amazon Q Developer to niezbędne narzędzie dla każdego programisty pracującego z AWS. Dzięki głębokiej wiedzy o serwisach AWS, wbudowanemu skanowaniu bezpieczeństwa i możliwościom transformacji kodu, Amazon Q znacząco przyspiesza rozwój aplikacji cloud-native.

Kluczowe zalety:

  • Głęboka integracja AWS - Zna wszystkie 200+ serwisów
  • Security-first - Automatyczne wykrywanie luk
  • IaC generation - CloudFormation, CDK, Terraform
  • Code modernization - Java, .NET transformation
  • Darmowy tier - Dostępny dla wszystkich

Jeśli budujesz na AWS, Amazon Q powinien być częścią Twojego toolkit.


Amazon Q Developer - complete guide to AI for AWS

What is Amazon Q Developer?

Amazon Q Developer (formerly Amazon CodeWhisperer) is an advanced AI assistant from Amazon Web Services, designed specifically for developers working with the AWS ecosystem. Unlike other AI assistants, Amazon Q has deep knowledge of all AWS services and can generate code that follows AWS best practices, is secure, and is optimized for cost.

Amazon Q is not just code autocomplete - it is a comprehensive tool for building, debugging, optimizing, and securing cloud-native applications. It integrates directly with the AWS Console, IDEs, and CLI, providing assistance at every stage of application development.

Why Amazon Q Developer?

Key advantages of Amazon Q

  1. Deep AWS knowledge - Knows all 200+ AWS services
  2. Security scanning - Automatic vulnerability detection
  3. Code transformation - Automatic code modernization (e.g., Java 8 to 17)
  4. Infrastructure as Code - Generation of CloudFormation, CDK, Terraform
  5. Cost optimization - Cost optimization suggestions
  6. Free tier - Free access for individual developers
  7. Compliance - SOC 2, HIPAA, FedRAMP compliant

Amazon Q vs other AI assistants for cloud

AspectAmazon QGitHub CopilotGoogle Cloud AIAzure AI
AWS IntegrationNativeNoneNoneNone
Security ScanningBuilt-inSeparate productLimitedLimited
IaC GenerationCloudFormation, CDKBasicTerraformARM, Bicep
Cost AnalysisYesNoNoNo
Free TierYesNoYesYes
Console IntegrationFullNoneFullFull

Installation and configuration

VS Code

Code
Bash
# 1. Open VS Code
# 2. Extensions (Ctrl+Shift+X)
# 3. Search "Amazon Q"
# 4. Install "Amazon Q"

# Or from the terminal:
code --install-extension amazonwebservices.amazon-q-vscode

JetBrains IDEs

Code
Bash
# 1. File → Settings → Plugins
# 2. Marketplace → Search "Amazon Q"
# 3. Install → Restart IDE

Visual Studio

Code
Bash
# 1. Extensions → Manage Extensions
# 2. Online → Search "AWS Toolkit"
# 3. Download → Restart VS

AWS CLI integration

Code
Bash
# Install AWS CLI
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
unzip awscliv2.zip
sudo ./aws/install

# Configuration
aws configure
# AWS Access Key ID: YOUR_ACCESS_KEY
# AWS Secret Access Key: YOUR_SECRET_KEY
# Default region name: eu-central-1
# Default output format: json

# Install Amazon Q CLI
pip install amazon-q-developer-cli

Authorization

Code
Bash
# Option 1: AWS Builder ID (free)
# - Click "Sign in" in the extension
# - Select "Use personal email"
# - Create an AWS Builder ID

# Option 2: AWS IAM Identity Center (enterprise)
# - Click "Sign in"
# - Select "Use IAM Identity Center"
# - Enter your organization's Start URL

AWS code generation

Lambda Functions

Code
TypeScript
// Amazon Q understands Lambda context and the AWS SDK

// Prompt: "Create Lambda function to process S3 events"

import { S3Event, Context } from 'aws-lambda'
import { S3Client, GetObjectCommand, PutObjectCommand } from '@aws-sdk/client-s3'

const s3Client = new S3Client({ region: process.env.AWS_REGION })

export const handler = async (event: S3Event, context: Context) => {
  console.log('Processing S3 event:', JSON.stringify(event, null, 2))

  for (const record of event.Records) {
    const bucket = record.s3.bucket.name
    const key = decodeURIComponent(record.s3.object.key.replace(/\+/g, ' '))

    try {
      // Get the object from S3
      const getCommand = new GetObjectCommand({ Bucket: bucket, Key: key })
      const response = await s3Client.send(getCommand)

      // Process the data
      const body = await response.Body?.transformToString()
      const processedData = processData(body)

      // Save the processed file
      const putCommand = new PutObjectCommand({
        Bucket: bucket,
        Key: `processed/${key}`,
        Body: JSON.stringify(processedData),
        ContentType: 'application/json'
      })
      await s3Client.send(putCommand)

      console.log(`Successfully processed: ${key}`)
    } catch (error) {
      console.error(`Error processing ${key}:`, error)
      throw error
    }
  }

  return {
    statusCode: 200,
    body: JSON.stringify({ message: 'Processing complete' })
  }
}

function processData(data: string | undefined): object {
  if (!data) return {}
  // Amazon Q suggests processing logic
  return JSON.parse(data)
}

DynamoDB operations

Code
Python
# Amazon Q knows DynamoDB best practices

# Prompt: "Create DynamoDB CRUD operations with optimistic locking"

import boto3
from boto3.dynamodb.conditions import Key, Attr
from botocore.exceptions import ClientError
from decimal import Decimal
import json

dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table('Users')

def create_user(user_data: dict) -> dict:
    """Create a new user with initial version."""
    user_data['version'] = 1
    user_data['created_at'] = datetime.now().isoformat()
    user_data['updated_at'] = datetime.now().isoformat()

    try:
        table.put_item(
            Item=user_data,
            ConditionExpression='attribute_not_exists(user_id)'
        )
        return {'success': True, 'user': user_data}
    except ClientError as e:
        if e.response['Error']['Code'] == 'ConditionalCheckFailedException':
            return {'success': False, 'error': 'User already exists'}
        raise


def get_user(user_id: str) -> dict:
    """Get user by ID with consistent read."""
    response = table.get_item(
        Key={'user_id': user_id},
        ConsistentRead=True
    )
    return response.get('Item')


def update_user(user_id: str, updates: dict, current_version: int) -> dict:
    """Update user with optimistic locking."""
    try:
        # Build update expression dynamically
        update_expr = 'SET version = version + :inc, updated_at = :updated'
        expr_values = {
            ':inc': 1,
            ':updated': datetime.now().isoformat(),
            ':current_version': current_version
        }
        expr_names = {}

        for key, value in updates.items():
            safe_key = f'#{key}'
            update_expr += f', {safe_key} = :{key}'
            expr_values[f':{key}'] = value
            expr_names[safe_key] = key

        response = table.update_item(
            Key={'user_id': user_id},
            UpdateExpression=update_expr,
            ExpressionAttributeValues=expr_values,
            ExpressionAttributeNames=expr_names if expr_names else None,
            ConditionExpression='version = :current_version',
            ReturnValues='ALL_NEW'
        )
        return {'success': True, 'user': response['Attributes']}

    except ClientError as e:
        if e.response['Error']['Code'] == 'ConditionalCheckFailedException':
            return {'success': False, 'error': 'Version conflict - retry required'}
        raise


def query_users_by_email_domain(domain: str) -> list:
    """Query users by email domain using GSI."""
    response = table.query(
        IndexName='email-domain-index',
        KeyConditionExpression=Key('email_domain').eq(domain),
        FilterExpression=Attr('active').eq(True)
    )
    return response['Items']


def batch_get_users(user_ids: list) -> list:
    """Batch get multiple users efficiently."""
    # DynamoDB limit: 100 items per batch
    results = []
    for i in range(0, len(user_ids), 100):
        batch = user_ids[i:i+100]
        response = dynamodb.batch_get_item(
            RequestItems={
                'Users': {
                    'Keys': [{'user_id': uid} for uid in batch],
                    'ConsistentRead': True
                }
            }
        )
        results.extend(response['Responses']['Users'])

        # Handle unprocessed keys
        while response.get('UnprocessedKeys'):
            response = dynamodb.batch_get_item(
                RequestItems=response['UnprocessedKeys']
            )
            results.extend(response['Responses']['Users'])

    return results

API Gateway + Lambda

Code
TypeScript
// Amazon Q generates complete APIs with validation

// Prompt: "Create REST API with API Gateway and Lambda"

import {
  APIGatewayProxyEvent,
  APIGatewayProxyResult,
  Context
} from 'aws-lambda'
import { DynamoDBClient } from '@aws-sdk/client-dynamodb'
import {
  DynamoDBDocumentClient,
  GetCommand,
  PutCommand,
  UpdateCommand,
  DeleteCommand,
  QueryCommand
} from '@aws-sdk/lib-dynamodb'

const client = new DynamoDBClient({})
const docClient = DynamoDBDocumentClient.from(client)
const TABLE_NAME = process.env.TABLE_NAME!

// Middleware for CORS and error handling
const withMiddleware = (
  handler: (event: APIGatewayProxyEvent) => Promise<any>
) => {
  return async (
    event: APIGatewayProxyEvent,
    context: Context
  ): Promise<APIGatewayProxyResult> => {
    const headers = {
      'Content-Type': 'application/json',
      'Access-Control-Allow-Origin': '*',
      'Access-Control-Allow-Headers': 'Content-Type,Authorization',
      'Access-Control-Allow-Methods': 'GET,POST,PUT,DELETE,OPTIONS'
    }

    // Handle preflight
    if (event.httpMethod === 'OPTIONS') {
      return { statusCode: 200, headers, body: '' }
    }

    try {
      const result = await handler(event)
      return {
        statusCode: result.statusCode || 200,
        headers,
        body: JSON.stringify(result.body)
      }
    } catch (error: any) {
      console.error('Error:', error)
      return {
        statusCode: error.statusCode || 500,
        headers,
        body: JSON.stringify({
          error: error.message || 'Internal Server Error'
        })
      }
    }
  }
}

// GET /users/{id}
export const getUser = withMiddleware(async (event) => {
  const userId = event.pathParameters?.id

  if (!userId) {
    throw { statusCode: 400, message: 'User ID is required' }
  }

  const command = new GetCommand({
    TableName: TABLE_NAME,
    Key: { pk: `USER#${userId}`, sk: `USER#${userId}` }
  })

  const result = await docClient.send(command)

  if (!result.Item) {
    throw { statusCode: 404, message: 'User not found' }
  }

  return { body: result.Item }
})

// POST /users
export const createUser = withMiddleware(async (event) => {
  const body = JSON.parse(event.body || '{}')

  // Validation
  if (!body.email || !body.name) {
    throw { statusCode: 400, message: 'Email and name are required' }
  }

  const userId = generateId()
  const timestamp = new Date().toISOString()

  const user = {
    pk: `USER#${userId}`,
    sk: `USER#${userId}`,
    id: userId,
    email: body.email,
    name: body.name,
    createdAt: timestamp,
    updatedAt: timestamp,
    GSI1PK: `EMAIL#${body.email}`,
    GSI1SK: `USER#${userId}`
  }

  const command = new PutCommand({
    TableName: TABLE_NAME,
    Item: user,
    ConditionExpression: 'attribute_not_exists(pk)'
  })

  await docClient.send(command)

  return { statusCode: 201, body: user }
})

// PUT /users/{id}
export const updateUser = withMiddleware(async (event) => {
  const userId = event.pathParameters?.id
  const body = JSON.parse(event.body || '{}')

  if (!userId) {
    throw { statusCode: 400, message: 'User ID is required' }
  }

  const updateExpressions: string[] = []
  const expressionValues: Record<string, any> = {}
  const expressionNames: Record<string, string> = {}

  if (body.name) {
    updateExpressions.push('#name = :name')
    expressionValues[':name'] = body.name
    expressionNames['#name'] = 'name'
  }

  if (body.email) {
    updateExpressions.push('email = :email')
    expressionValues[':email'] = body.email
  }

  updateExpressions.push('updatedAt = :updatedAt')
  expressionValues[':updatedAt'] = new Date().toISOString()

  const command = new UpdateCommand({
    TableName: TABLE_NAME,
    Key: { pk: `USER#${userId}`, sk: `USER#${userId}` },
    UpdateExpression: `SET ${updateExpressions.join(', ')}`,
    ExpressionAttributeValues: expressionValues,
    ExpressionAttributeNames: Object.keys(expressionNames).length > 0
      ? expressionNames
      : undefined,
    ReturnValues: 'ALL_NEW',
    ConditionExpression: 'attribute_exists(pk)'
  })

  const result = await docClient.send(command)
  return { body: result.Attributes }
})

// DELETE /users/{id}
export const deleteUser = withMiddleware(async (event) => {
  const userId = event.pathParameters?.id

  if (!userId) {
    throw { statusCode: 400, message: 'User ID is required' }
  }

  const command = new DeleteCommand({
    TableName: TABLE_NAME,
    Key: { pk: `USER#${userId}`, sk: `USER#${userId}` },
    ConditionExpression: 'attribute_exists(pk)'
  })

  await docClient.send(command)
  return { statusCode: 204, body: null }
})

function generateId(): string {
  return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`
}

Infrastructure as Code

CloudFormation

Code
YAML
# Amazon Q generates complete CloudFormation templates

# Prompt: "Create CloudFormation template for serverless API"

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: Serverless API with Lambda, API Gateway, and DynamoDB

Parameters:
  Environment:
    Type: String
    Default: dev
    AllowedValues:
      - dev
      - staging
      - prod

  TableReadCapacity:
    Type: Number
    Default: 5

  TableWriteCapacity:
    Type: Number
    Default: 5

Globals:
  Function:
    Runtime: nodejs18.x
    Timeout: 30
    MemorySize: 256
    Environment:
      Variables:
        TABLE_NAME: !Ref UsersTable
        ENVIRONMENT: !Ref Environment
    Tracing: Active

Resources:
  # DynamoDB Table
  UsersTable:
    Type: AWS::DynamoDB::Table
    Properties:
      TableName: !Sub ${AWS::StackName}-users
      BillingMode: PROVISIONED
      ProvisionedThroughput:
        ReadCapacityUnits: !Ref TableReadCapacity
        WriteCapacityUnits: !Ref TableWriteCapacity
      AttributeDefinitions:
        - AttributeName: pk
          AttributeType: S
        - AttributeName: sk
          AttributeType: S
        - AttributeName: GSI1PK
          AttributeType: S
        - AttributeName: GSI1SK
          AttributeType: S
      KeySchema:
        - AttributeName: pk
          KeyType: HASH
        - AttributeName: sk
          KeyType: RANGE
      GlobalSecondaryIndexes:
        - IndexName: GSI1
          KeySchema:
            - AttributeName: GSI1PK
              KeyType: HASH
            - AttributeName: GSI1SK
              KeyType: RANGE
          Projection:
            ProjectionType: ALL
          ProvisionedThroughput:
            ReadCapacityUnits: !Ref TableReadCapacity
            WriteCapacityUnits: !Ref TableWriteCapacity
      PointInTimeRecoverySpecification:
        PointInTimeRecoveryEnabled: true
      SSESpecification:
        SSEEnabled: true

  # API Gateway
  ApiGateway:
    Type: AWS::Serverless::Api
    Properties:
      Name: !Sub ${AWS::StackName}-api
      StageName: !Ref Environment
      Cors:
        AllowMethods: "'GET,POST,PUT,DELETE,OPTIONS'"
        AllowHeaders: "'Content-Type,Authorization'"
        AllowOrigin: "'*'"
      Auth:
        DefaultAuthorizer: CognitoAuthorizer
        Authorizers:
          CognitoAuthorizer:
            UserPoolArn: !GetAtt UserPool.Arn

  # Lambda Functions
  GetUserFunction:
    Type: AWS::Serverless::Function
    Properties:
      FunctionName: !Sub ${AWS::StackName}-get-user
      Handler: dist/handlers/users.getUser
      CodeUri: ./
      Policies:
        - DynamoDBReadPolicy:
            TableName: !Ref UsersTable
      Events:
        Api:
          Type: Api
          Properties:
            RestApiId: !Ref ApiGateway
            Path: /users/{id}
            Method: GET

  CreateUserFunction:
    Type: AWS::Serverless::Function
    Properties:
      FunctionName: !Sub ${AWS::StackName}-create-user
      Handler: dist/handlers/users.createUser
      CodeUri: ./
      Policies:
        - DynamoDBCrudPolicy:
            TableName: !Ref UsersTable
      Events:
        Api:
          Type: Api
          Properties:
            RestApiId: !Ref ApiGateway
            Path: /users
            Method: POST

  UpdateUserFunction:
    Type: AWS::Serverless::Function
    Properties:
      FunctionName: !Sub ${AWS::StackName}-update-user
      Handler: dist/handlers/users.updateUser
      CodeUri: ./
      Policies:
        - DynamoDBCrudPolicy:
            TableName: !Ref UsersTable
      Events:
        Api:
          Type: Api
          Properties:
            RestApiId: !Ref ApiGateway
            Path: /users/{id}
            Method: PUT

  DeleteUserFunction:
    Type: AWS::Serverless::Function
    Properties:
      FunctionName: !Sub ${AWS::StackName}-delete-user
      Handler: dist/handlers/users.deleteUser
      CodeUri: ./
      Policies:
        - DynamoDBCrudPolicy:
            TableName: !Ref UsersTable
      Events:
        Api:
          Type: Api
          Properties:
            RestApiId: !Ref ApiGateway
            Path: /users/{id}
            Method: DELETE

  # Cognito User Pool
  UserPool:
    Type: AWS::Cognito::UserPool
    Properties:
      UserPoolName: !Sub ${AWS::StackName}-users
      AutoVerifiedAttributes:
        - email
      UsernameAttributes:
        - email
      Policies:
        PasswordPolicy:
          MinimumLength: 8
          RequireLowercase: true
          RequireNumbers: true
          RequireSymbols: true
          RequireUppercase: true

  UserPoolClient:
    Type: AWS::Cognito::UserPoolClient
    Properties:
      ClientName: !Sub ${AWS::StackName}-client
      UserPoolId: !Ref UserPool
      GenerateSecret: false
      ExplicitAuthFlows:
        - ALLOW_USER_PASSWORD_AUTH
        - ALLOW_REFRESH_TOKEN_AUTH

Outputs:
  ApiUrl:
    Description: API Gateway URL
    Value: !Sub https://${ApiGateway}.execute-api.${AWS::Region}.amazonaws.com/${Environment}
    Export:
      Name: !Sub ${AWS::StackName}-api-url

  TableName:
    Description: DynamoDB Table Name
    Value: !Ref UsersTable
    Export:
      Name: !Sub ${AWS::StackName}-table-name

  UserPoolId:
    Description: Cognito User Pool ID
    Value: !Ref UserPool
    Export:
      Name: !Sub ${AWS::StackName}-user-pool-id

  UserPoolClientId:
    Description: Cognito User Pool Client ID
    Value: !Ref UserPoolClient
    Export:
      Name: !Sub ${AWS::StackName}-user-pool-client-id

AWS CDK

Code
TypeScript
// Amazon Q generates complete CDK stacks

// Prompt: "Create CDK stack for ECS Fargate service"

import * as cdk from 'aws-cdk-lib'
import { Construct } from 'constructs'
import * as ec2 from 'aws-cdk-lib/aws-ec2'
import * as ecs from 'aws-cdk-lib/aws-ecs'
import * as ecr from 'aws-cdk-lib/aws-ecr'
import * as elbv2 from 'aws-cdk-lib/aws-elasticloadbalancingv2'
import * as logs from 'aws-cdk-lib/aws-logs'
import * as secretsmanager from 'aws-cdk-lib/aws-secretsmanager'
import * as rds from 'aws-cdk-lib/aws-rds'

interface ApiStackProps extends cdk.StackProps {
  environment: 'dev' | 'staging' | 'prod'
  domainName?: string
}

export class ApiStack extends cdk.Stack {
  public readonly loadBalancer: elbv2.ApplicationLoadBalancer
  public readonly service: ecs.FargateService

  constructor(scope: Construct, id: string, props: ApiStackProps) {
    super(scope, id, props)

    // VPC
    const vpc = new ec2.Vpc(this, 'Vpc', {
      maxAzs: 3,
      natGateways: props.environment === 'prod' ? 3 : 1,
      subnetConfiguration: [
        {
          name: 'Public',
          subnetType: ec2.SubnetType.PUBLIC,
          cidrMask: 24
        },
        {
          name: 'Private',
          subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS,
          cidrMask: 24
        },
        {
          name: 'Isolated',
          subnetType: ec2.SubnetType.PRIVATE_ISOLATED,
          cidrMask: 24
        }
      ]
    })

    // Security Groups
    const albSecurityGroup = new ec2.SecurityGroup(this, 'AlbSg', {
      vpc,
      description: 'ALB Security Group',
      allowAllOutbound: true
    })
    albSecurityGroup.addIngressRule(
      ec2.Peer.anyIpv4(),
      ec2.Port.tcp(443),
      'HTTPS'
    )
    albSecurityGroup.addIngressRule(
      ec2.Peer.anyIpv4(),
      ec2.Port.tcp(80),
      'HTTP redirect'
    )

    const ecsSecurityGroup = new ec2.SecurityGroup(this, 'EcsSg', {
      vpc,
      description: 'ECS Security Group',
      allowAllOutbound: true
    })
    ecsSecurityGroup.addIngressRule(
      albSecurityGroup,
      ec2.Port.tcp(3000),
      'From ALB'
    )

    const dbSecurityGroup = new ec2.SecurityGroup(this, 'DbSg', {
      vpc,
      description: 'RDS Security Group'
    })
    dbSecurityGroup.addIngressRule(
      ecsSecurityGroup,
      ec2.Port.tcp(5432),
      'From ECS'
    )

    // RDS PostgreSQL
    const dbSecret = new secretsmanager.Secret(this, 'DbSecret', {
      secretName: `${id}/database`,
      generateSecretString: {
        secretStringTemplate: JSON.stringify({ username: 'admin' }),
        generateStringKey: 'password',
        excludePunctuation: true
      }
    })

    const database = new rds.DatabaseInstance(this, 'Database', {
      engine: rds.DatabaseInstanceEngine.postgres({
        version: rds.PostgresEngineVersion.VER_15
      }),
      instanceType: ec2.InstanceType.of(
        ec2.InstanceClass.T3,
        props.environment === 'prod'
          ? ec2.InstanceSize.MEDIUM
          : ec2.InstanceSize.MICRO
      ),
      vpc,
      vpcSubnets: { subnetType: ec2.SubnetType.PRIVATE_ISOLATED },
      securityGroups: [dbSecurityGroup],
      credentials: rds.Credentials.fromSecret(dbSecret),
      multiAz: props.environment === 'prod',
      allocatedStorage: 20,
      maxAllocatedStorage: 100,
      deletionProtection: props.environment === 'prod',
      removalPolicy: props.environment === 'prod'
        ? cdk.RemovalPolicy.RETAIN
        : cdk.RemovalPolicy.DESTROY
    })

    // ECS Cluster
    const cluster = new ecs.Cluster(this, 'Cluster', {
      vpc,
      containerInsights: true,
      enableFargateCapacityProviders: true
    })

    // ECR Repository
    const repository = new ecr.Repository(this, 'Repository', {
      repositoryName: `${id.toLowerCase()}-api`,
      removalPolicy: cdk.RemovalPolicy.DESTROY,
      imageScanOnPush: true,
      lifecycleRules: [
        {
          maxImageCount: 10,
          rulePriority: 1
        }
      ]
    })

    // Task Definition
    const taskDefinition = new ecs.FargateTaskDefinition(this, 'TaskDef', {
      memoryLimitMiB: props.environment === 'prod' ? 2048 : 512,
      cpu: props.environment === 'prod' ? 1024 : 256
    })

    const container = taskDefinition.addContainer('api', {
      image: ecs.ContainerImage.fromEcrRepository(repository, 'latest'),
      logging: ecs.LogDrivers.awsLogs({
        streamPrefix: 'api',
        logRetention: logs.RetentionDays.ONE_MONTH
      }),
      environment: {
        NODE_ENV: props.environment,
        PORT: '3000'
      },
      secrets: {
        DATABASE_URL: ecs.Secret.fromSecretsManager(database.secret!),
        DB_HOST: ecs.Secret.fromSecretsManager(database.secret!, 'host'),
        DB_PASSWORD: ecs.Secret.fromSecretsManager(database.secret!, 'password')
      },
      healthCheck: {
        command: ['CMD-SHELL', 'curl -f http://localhost:3000/health || exit 1'],
        interval: cdk.Duration.seconds(30),
        timeout: cdk.Duration.seconds(5),
        retries: 3
      }
    })

    container.addPortMappings({ containerPort: 3000 })

    // Fargate Service
    this.service = new ecs.FargateService(this, 'Service', {
      cluster,
      taskDefinition,
      desiredCount: props.environment === 'prod' ? 3 : 1,
      securityGroups: [ecsSecurityGroup],
      vpcSubnets: { subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS },
      capacityProviderStrategies: [
        {
          capacityProvider: 'FARGATE_SPOT',
          weight: props.environment === 'prod' ? 1 : 2
        },
        {
          capacityProvider: 'FARGATE',
          weight: props.environment === 'prod' ? 2 : 1
        }
      ],
      circuitBreaker: { rollback: true },
      enableExecuteCommand: true
    })

    // Auto Scaling
    const scaling = this.service.autoScaleTaskCount({
      minCapacity: props.environment === 'prod' ? 3 : 1,
      maxCapacity: props.environment === 'prod' ? 20 : 5
    })

    scaling.scaleOnCpuUtilization('CpuScaling', {
      targetUtilizationPercent: 70,
      scaleInCooldown: cdk.Duration.seconds(60),
      scaleOutCooldown: cdk.Duration.seconds(60)
    })

    scaling.scaleOnMemoryUtilization('MemoryScaling', {
      targetUtilizationPercent: 70
    })

    // Application Load Balancer
    this.loadBalancer = new elbv2.ApplicationLoadBalancer(this, 'Alb', {
      vpc,
      internetFacing: true,
      securityGroup: albSecurityGroup
    })

    const listener = this.loadBalancer.addListener('HttpsListener', {
      port: 443,
      certificates: props.domainName
        ? [/* Add certificate */]
        : undefined,
      protocol: props.domainName
        ? elbv2.ApplicationProtocol.HTTPS
        : elbv2.ApplicationProtocol.HTTP
    })

    listener.addTargets('EcsTarget', {
      port: 3000,
      targets: [this.service],
      healthCheck: {
        path: '/health',
        interval: cdk.Duration.seconds(30),
        healthyThresholdCount: 2,
        unhealthyThresholdCount: 3
      },
      deregistrationDelay: cdk.Duration.seconds(30)
    })

    // HTTP to HTTPS redirect
    if (props.domainName) {
      this.loadBalancer.addListener('HttpListener', {
        port: 80,
        defaultAction: elbv2.ListenerAction.redirect({
          port: '443',
          protocol: 'HTTPS',
          permanent: true
        })
      })
    }

    // Outputs
    new cdk.CfnOutput(this, 'LoadBalancerDns', {
      value: this.loadBalancer.loadBalancerDnsName
    })

    new cdk.CfnOutput(this, 'RepositoryUri', {
      value: repository.repositoryUri
    })
  }
}

Security scanning

Automatic code scanning

Code
TypeScript
// Amazon Q automatically detects security issues

// ⚠️ Amazon Q warns:
// SQL Injection vulnerability detected
const query = `SELECT * FROM users WHERE id = '${userId}'`

// ✅ Amazon Q suggests a fix:
import { DynamoDBDocumentClient, GetCommand } from '@aws-sdk/lib-dynamodb'

const command = new GetCommand({
  TableName: 'users',
  Key: { id: userId }
})
const result = await docClient.send(command)

Types of detected issues

Code
TEXT
┌─────────────────────────────────────────────────────────┐
│           AMAZON Q SECURITY SCANNING                     │
├─────────────────────────────────────────────────────────┤
│                                                         │
│   ⚠️ Injection Vulnerabilities                          │
│      └─ SQL, NoSQL, Command, LDAP injection            │
│                                                         │
│   ⚠️ Authentication Issues                              │
│      └─ Hardcoded credentials, weak passwords          │
│                                                         │
│   ⚠️ Sensitive Data Exposure                            │
│      └─ API keys, secrets in code                      │
│                                                         │
│   ⚠️ Security Misconfiguration                          │
│      └─ Open S3 buckets, public resources             │
│                                                         │
│   ⚠️ Cross-Site Scripting (XSS)                         │
│      └─ Unescaped user input                           │
│                                                         │
│   ⚠️ Insecure Dependencies                              │
│      └─ Known CVEs in packages                         │
│                                                         │
└─────────────────────────────────────────────────────────┘

Scanning in CI/CD

.github/workflows/security-scan.yml
YAML
# .github/workflows/security-scan.yml
name: Security Scan

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

jobs:
  amazon-q-scan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Configure AWS Credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: eu-central-1

      - name: Run Amazon Q Security Scan
        run: |
          pip install amazon-q-developer-cli
          amazon-q scan --format sarif --output results.sarif

      - name: Upload SARIF results
        uses: github/codeql-action/upload-sarif@v2
        with:
          sarif_file: results.sarif

      - name: Fail on high severity
        run: |
          HIGH_COUNT=$(jq '.runs[0].results | map(select(.level == "error")) | length' results.sarif)
          if [ "$HIGH_COUNT" -gt 0 ]; then
            echo "Found $HIGH_COUNT high severity issues"
            exit 1
          fi

Code transformation

Java modernization

Code
Java
// Amazon Q can automatically modernize Java code

// BEFORE (Java 8):
public class UserService {
    private List<User> users = new ArrayList<>();

    public List<User> findActiveUsers() {
        List<User> result = new ArrayList<>();
        for (User user : users) {
            if (user.isActive()) {
                result.add(user);
            }
        }
        return result;
    }

    public Optional<User> findByEmail(String email) {
        for (User user : users) {
            if (user.getEmail().equals(email)) {
                return Optional.of(user);
            }
        }
        return Optional.empty();
    }
}

// AFTER (Java 17) - Amazon Q transformation:
public class UserService {
    private final List<User> users = new ArrayList<>();

    public List<User> findActiveUsers() {
        return users.stream()
            .filter(User::isActive)
            .toList();
    }

    public Optional<User> findByEmail(String email) {
        return users.stream()
            .filter(user -> user.getEmail().equals(email))
            .findFirst();
    }
}

.NET modernization

Code
C#
// Amazon Q modernizes .NET Framework to .NET 8

// BEFORE (.NET Framework 4.8):
public class DataService
{
    public async Task<List<Customer>> GetCustomersAsync()
    {
        using (var client = new HttpClient())
        {
            var response = await client.GetAsync("https://api.example.com/customers");
            var content = await response.Content.ReadAsStringAsync();
            return JsonConvert.DeserializeObject<List<Customer>>(content);
        }
    }
}

// AFTER (.NET 8) - Amazon Q transformation:
public class DataService(HttpClient httpClient)
{
    public async Task<List<Customer>> GetCustomersAsync()
    {
        return await httpClient.GetFromJsonAsync<List<Customer>>(
            "https://api.example.com/customers"
        ) ?? [];
    }
}

Amazon Q in the AWS Console

Chat in the Console

Code
TEXT
User: How do I set up a VPC with public and private subnets?

Amazon Q:
I'll help you create a VPC with public and private subnets. Here's what you need:

1. **VPC**: Create a VPC with CIDR block (e.g., 10.0.0.0/16)

2. **Subnets**:
   - Public: 10.0.1.0/24 (for ALB, NAT Gateway)
   - Private: 10.0.2.0/24 (for EC2, Lambda)

3. **Internet Gateway**: Attach to VPC for public internet access

4. **NAT Gateway**: Place in public subnet for private subnet outbound access

5. **Route Tables**:
   - Public: Route 0.0.0.0/0 → Internet Gateway
   - Private: Route 0.0.0.0/0 → NAT Gateway

Would you like me to create a CloudFormation template for this setup?

Troubleshooting with Amazon Q

Code
TEXT
User: My Lambda function is timing out when connecting to RDS

Amazon Q:
Based on the symptoms, here are the most common causes and solutions:

**1. VPC Configuration Issue (Most Common)**
- Lambda needs to be in the same VPC as RDS
- Check Security Groups allow traffic on port 5432/3306
- Ensure Lambda has a route to RDS subnet

**2. Security Group Configuration**

RDS Security Group should allow:

Inbound: Port 5432 from Lambda Security Group

Code
TEXT
**3. NAT Gateway for Internet Access**
If Lambda needs both RDS and internet access, ensure:
- Lambda is in private subnet
- NAT Gateway is in public subnet
- Route table has 0.0.0.0/0 → NAT Gateway

**4. Connection Pool Exhaustion**
```python
# Use connection pooling
from sqlalchemy import create_engine
engine = create_engine(DATABASE_URL, pool_size=5, max_overflow=10)

5. Increase Timeout

Code
YAML
# SAM template
Timeout: 30  # Increase from default 3 seconds

Would you like me to help diagnose which specific issue you're facing?

Code
TEXT
## Pricing

### Free Tier

**Price**: $0/month

**Includes:**
- 50 security scans/month
- Basic code suggestions
- VS Code and JetBrains integration
- Access to Amazon Q Chat
- AWS Console integration

### Pro Tier

**Price**: $19/user/month

**Includes:**
- Everything from Free
- Unlimited security scans
- Code transformation
- Administrative controls
- Higher quotas
- Priority support

### Enterprise

**Price**: Custom

**Includes:**
- Everything from Pro
- SSO/SAML
- Customization options
- Dedicated support
- SLA guarantees
- Compliance reporting

## Best practices

### Effective prompts

```typescript
// ❌ Too generic
// "Create Lambda function"

// ✅ Specific
// "Create Lambda function that:
//  - Processes S3 events for image uploads
//  - Resizes images to 800x600
//  - Stores thumbnails in a separate bucket
//  - Uses Sharp library for image processing
//  - Includes error handling and CloudWatch logging"

Project configuration

.amazonq/config.json
JSON
// .amazonq/config.json
{
  "codeGeneration": {
    "preferredLanguage": "typescript",
    "preferredRuntime": "nodejs18.x",
    "preferredRegion": "eu-central-1"
  },
  "securityScanning": {
    "enabled": true,
    "severity": "HIGH",
    "autoFix": false
  },
  "infraAsCode": {
    "preferredTool": "cdk",
    "language": "typescript"
  }
}

FAQ

Is Amazon Q free?

Yes, Amazon Q has a free tier with 50 security scans per month and basic code suggestions. The Pro tier costs $19/user/month.

Does Amazon Q only work with AWS?

Amazon Q is optimized for AWS, but it can generate general-purpose code in many languages. However, its greatest value lies in its deep integration with the AWS ecosystem.

How does Amazon Q differ from Copilot?

Amazon Q specializes in AWS - it knows all the services, best practices, and security patterns. Copilot is more general-purpose, but it does not have such deep knowledge of AWS.

Can I use Amazon Q offline?

No, Amazon Q requires an internet connection to generate suggestions and perform security scanning.

Summary

Amazon Q Developer is an essential tool for any developer working with AWS. Thanks to its deep knowledge of AWS services, built-in security scanning, and code transformation capabilities, Amazon Q significantly accelerates cloud-native application development.

Key advantages:

  • Deep AWS integration - Knows all 200+ services
  • Security-first - Automatic vulnerability detection
  • IaC generation - CloudFormation, CDK, Terraform
  • Code modernization - Java, .NET transformation
  • Free tier - Available for everyone

If you are building on AWS, Amazon Q should be part of your toolkit.