DevOps & Cloud

AWS CloudFormation: Infrastructure as Code

TOKEN

Panduan lengkap AWS CloudFormation β€” template YAML/JSON, stacks, change sets, stack sets, drift detection, dan best practices Infrastructure as Code

1. Pengenalan CloudFormation

AWS CloudFormation adalah layanan AWS yang memungkinkan Anda mendefinisikan infrastruktur cloud sebagai kode (Infrastructure as Code / IaC). Dengan CloudFormation, Anda dapat mendeskripsikan seluruh sumber daya AWS β€” EC2, RDS, VPC, S3, dan lainnya β€” dalam file template yang version-controlled, reproducible, dan auditable.

Konsep utamanya sederhana: Anda menulis template (dalam format YAML atau JSON) yang mendefinisikan infrastruktur yang diinginkan, lalu CloudFormation akan membuat, memperbarui, atau menghapus resource tersebut secara otomatis sesuai template. Ini menghilangkan konfigurasi manual melalui console dan memastikan infrastruktur dapat direplikasi secara konsisten.

Mengapa CloudFormation?

Keunggulan Penjelasan
Infrastructure as CodeInfrastruktur didefinisikan dalam kode yang bisa di-review, version, dan audit
ReproducibilityBuat environment identik berkali-kali dengan template yang sama
AutomationProvisioning dan teardown infrastruktur sepenuhnya otomatis
Dependency ManagementCloudFormation otomatis menentukan urutan pembuatan resource
RollbackJika deploy gagal, semua perubahan di-rollback secara otomatis
Change TrackingSetiap perubahan infrastructure tercatat dan bisa di-audit
Drift DetectionDetect perubahan manual di luar CloudFormation
Diagram: Cara Kerja CloudFormation
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                 AWS CloudFormation                        β”‚
β”‚                                                          β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
β”‚  β”‚  Template     │────►│  CloudFormation Service       β”‚  β”‚
β”‚  β”‚  (YAML/JSON)  β”‚     β”‚                              β”‚  β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜     β”‚  1. Parse template            β”‚  β”‚
β”‚                       β”‚  2. Validate resources         β”‚  β”‚
β”‚                       β”‚  3. Create change set          β”‚  β”‚
β”‚                       β”‚  4. Execute changes            β”‚  β”‚
β”‚                       β”‚  5. Track stack state          β”‚  β”‚
β”‚                       β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
β”‚                                  β”‚                       β”‚
β”‚              β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”       β”‚
β”‚              β–Ό                   β–Ό              β–Ό       β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
β”‚  β”‚   EC2 Instances β”‚ β”‚   RDS Database  β”‚ β”‚ S3 Bucket  β”‚  β”‚
β”‚  β”‚   VPC/Subnets   β”‚ β”‚   ElastiCache   β”‚ β”‚ IAM Roles  β”‚  β”‚
β”‚  β”‚   Security Grps β”‚ β”‚   Lambda Funcs  β”‚ β”‚ CloudWatch  β”‚  β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
β”‚                                                          β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚
β”‚  β”‚  Stack: men-track semua resource yang dibuat     β”‚   β”‚
β”‚  β”‚  - Resource physical IDs                        β”‚   β”‚
β”‚  β”‚  - Status (CREATE_COMPLETE, UPDATE_IN_PROGRESS) β”‚   β”‚
β”‚  β”‚  - Event history                                β”‚   β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Perbandingan CloudFormation dengan tools IaC lain:

Fitur CloudFormation Terraform Pulumi
VendorAWS onlyMulti-cloudMulti-cloud
BahasaYAML/JSONHCLPython/TypeScript/Go
State ManagementManaged oleh AWSSelf-managedManaged atau self
CostGratis (bayar resource)Open-sourceOpen-source
Custom ResourcesLambda-backedProvidersNative code
πŸ’‘ Tips

Jika infrastruktur Anda 100% AWS, CloudFormation adalah pilihan yang sangat baik karena terintegrasi penuh dengan seluruh layanan AWS dan tidak memerlukan state management eksternal. Jika multi-cloud, pertimbangkan Terraform atau Pulumi.

2. Konsep Dasar: Resources, Parameters, Outputs

CloudFormation template terdiri dari beberapa komponen utama yang masing-masing memiliki peran penting dalam mendefinisikan infrastruktur Anda.

Bagian Utama Template

Diagram: Struktur CloudFormation Template
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚              CLOUDFORMATION TEMPLATE                         β”‚
β”‚                                                              β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚
β”‚  β”‚  AWSTemplateFormat  β”‚  β”‚  Description                β”‚   β”‚
β”‚  β”‚  Version: '2010-09' β”‚  β”‚  "Deskripsi template..."    β”‚   β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
β”‚                                                              β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚
β”‚  β”‚  Parameters         β”‚  β”‚  Mappings                   β”‚   β”‚
β”‚  β”‚  - Input untuk user β”‚  β”‚  - Lookup tables            β”‚   β”‚
β”‚  β”‚  - Type validation  β”‚  β”‚  - Region-based configs     β”‚   β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
β”‚                                                              β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚
β”‚  β”‚  Conditions         β”‚  β”‚  Resources β˜…                β”‚   β”‚
β”‚  β”‚  - If/else logic    β”‚  β”‚  - AWS resources (REQUIRED) β”‚   β”‚
β”‚  β”‚  - Environment flag β”‚  β”‚  - Core dari template       β”‚   β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
β”‚                                                              β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚
β”‚  β”‚  Outputs            β”‚  β”‚  Transform (opsional)      β”‚   β”‚
β”‚  β”‚  - Export values    β”‚  β”‚  - Macros, SAM, etc.       β”‚   β”‚
β”‚  β”‚  - Stack exports    β”‚  β”‚                              β”‚   β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Resources β€” Komponen Wajib

Resources adalah satu-satunya bagian wajib dalam template. Setiap resource merepresentasikan satu AWS service resource yang akan dibuat. Setiap resource memiliki tipe (misalnya AWS::EC2::Instance) dan properti spesifik.

YAML
Resources:
  # EC2 Instance
  WebServer:
    Type: AWS::EC2::Instance
    Properties:
      InstanceType: t3.micro
      ImageId: ami-0c55b159cbfafe1f0
      KeyName: my-key-pair
      SecurityGroups:
        - !Ref WebSecurityGroup
      Tags:
        - Key: Name
          Value: MyWebServer

  # Security Group
  WebSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Allow HTTP and SSH
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 80
          ToPort: 80
          CidrIp: 0.0.0.0/0
        - IpProtocol: tcp
          FromPort: 22
          ToPort: 22
          CidrIp: 0.0.0.0/0

  # S3 Bucket
  AppBucket:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: my-app-bucket-unique-name
      VersioningConfiguration:
        Status: Enabled
      Tags:
        - Key: Environment
          Value: Production

Parameters β€” Input dari User

Parameters memungkinkan Anda membuat template yang fleksibel dengan nilai yang bisa di-custom saat deploy. Parameter mendukung tipe data, default value, allowed values, dan constraints.

YAML
Parameters:
  InstanceType:
    Type: String
    Default: t3.micro
    AllowedValues:
      - t3.micro
      - t3.small
      - t3.medium
      - t3.large
    Description: Pilih tipe EC2 instance

  EnvironmentName:
    Type: String
    AllowedValues:
      - dev
      - staging
      - production
    Description: Nama environment
    ConstraintDescription: Harus dev, staging, atau production

  KeyPairName:
    Type: AWS::EC2::KeyPair::KeyName
    Description: Nama EC2 Key Pair untuk SSH

  DBPassword:
    Type: String
    NoEcho: true
    MinLength: 8
    MaxLength: 64
    Description: Password untuk database RDS
    AllowedPattern: '[a-zA-Z0-9]*'
    ConstraintDescription: Password harus alphanumeric

Outputs β€” Nilai yang Di-ekspor

Outputs mendefinisikan nilai yang akan ditampilkan setelah stack selesai dibuat, dan bisa di-export untuk digunakan oleh stack lain.

YAML
Outputs:
  InstancePublicIP:
    Description: IP Public dari EC2 instance
    Value: !GetAtt WebServer.PublicIp
    Export:
      Name: !Sub '${AWS::StackName}-InstanceIP'

  BucketARN:
    Description: ARN dari S3 bucket
    Value: !GetAtt AppBucket.Arn
    Export:
      Name: !Sub '${AWS::StackName}-BucketARN'

  SecurityGroupId:
    Description: ID Security Group
    Value: !Ref WebSecurityGroup
    Export:
      Name: !Sub '${AWS::StackName}-SGId'

  WebsiteURL:
    Description: URL website
    Value: !Sub 'http://${WebServer.PublicDnsName}'
    Export:
      Name: !Sub '${AWS::StackName}-URL'

Mappings β€” Lookup Tables

Mappings memungkinkan lookup berdasarkan region atau parameter, seperti tabel referensi statis.

YAML
Mappings:
  RegionMap:
    us-east-1:
      AMI: ami-0c55b159cbfafe1f0
      InstanceType: t3.micro
    us-west-2:
      AMI: ami-0c55b159cbfafe1f0
      InstanceType: t3.small
    ap-southeast-1:
      AMI: ami-0c55b159cbfafe1f0
      InstanceType: t3.micro
    eu-west-1:
      AMI: ami-0c55b159cbfafe1f0
      InstanceType: t3.medium

Conditions β€” Logika Kondisional

Conditions memungkinkan Anda membuat resource secara kondisional berdasarkan parameter atau pseudo-parameter.

YAML
Conditions:
  IsProduction: !Equals [!Ref EnvironmentName, production]
  IsUSRegion: !Contains
    - !Ref 'AWS::Region'
    - us
  CreateMonitoring: !Or
    - !Condition IsProduction
    - !Equals [!Ref EnableMonitoring, 'true']

Resources:
  # Hanya dibuat di production
  AlarmTopic:
    Type: AWS::SNS::Topic
    Condition: IsProduction
    Properties:
      TopicName: !Sub '${EnvironmentName}-alerts'

  # Dibuat berdasarkan kondisi
  CloudWatchAlarm:
    Type: AWS::CloudWatch::Alarm
    Condition: CreateMonitoring
    Properties:
      AlarmName: HighCPU
      MetricName: CPUUtilization
      Namespace: AWS/EC2
      Statistic: Average
      Period: 300
      EvaluationPeriods: 2
      Threshold: 80

3. Menulis Template YAML

YAML adalah format yang paling direkomendasikan untuk CloudFormation templates karena lebih mudah dibaca dan ditulis dibanding JSON. Berikut contoh template lengkap untuk membuat infrastruktur web server.

Template Lengkap: Web Server Stack

YAML
AWSTemplateFormatVersion: '2010-09-09'
Description: 'Web Server Stack - VPC, EC2, RDS, S3'

# ==============================
# PARAMETERS
# ==============================
Parameters:
  VpcCIDR:
    Type: String
    Default: 10.0.0.0/16
    Description: CIDR block untuk VPC

  PublicSubnet1CIDR:
    Type: String
    Default: 10.0.1.0/24

  PublicSubnet2CIDR:
    Type: String
    Default: 10.0.2.0/24

  InstanceType:
    Type: String
    Default: t3.micro
    AllowedValues:
      - t3.micro
      - t3.small
      - t3.medium
      - t3.large

  EnvironmentName:
    Type: String
    Default: dev
    AllowedValues:
      - dev
      - staging
      - production

# ==============================
# RESOURCES
# ==============================
Resources:

  # --- VPC ---
  VPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: !Ref VpcCIDR
      EnableDnsSupport: true
      EnableDnsHostnames: true
      Tags:
        - Key: Name
          Value: !Sub '${EnvironmentName}-VPC'
        - Key: Environment
          Value: !Ref EnvironmentName

  # --- Internet Gateway ---
  InternetGateway:
    Type: AWS::EC2::InternetGateway
    Properties:
      Tags:
        - Key: Name
          Value: !Sub '${EnvironmentName}-IGW'

  IGWAttachment:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      InternetGatewayId: !Ref InternetGateway
      VpcId: !Ref VPC

  # --- Public Subnet 1 ---
  PublicSubnet1:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      CidrBlock: !Ref PublicSubnet1CIDR
      AvailabilityZone: !Select [0, !GetAZs '']
      MapPublicIpOnLaunch: true
      Tags:
        - Key: Name
          Value: !Sub '${EnvironmentName}-PublicSubnet1'

  # --- Public Subnet 2 ---
  PublicSubnet2:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      CidrBlock: !Ref PublicSubnet2CIDR
      AvailabilityZone: !Select [1, !GetAZs '']
      MapPublicIpOnLaunch: true
      Tags:
        - Key: Name
          Value: !Sub '${EnvironmentName}-PublicSubnet2'

  # --- Route Table ---
  PublicRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: !Sub '${EnvironmentName}-PublicRT'

  PublicRoute:
    Type: AWS::EC2::Route
    DependsOn: IGWAttachment
    Properties:
      RouteTableId: !Ref PublicRouteTable
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref InternetGateway

  Subnet1RTAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PublicSubnet1
      RouteTableId: !Ref PublicRouteTable

  Subnet2RTAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PublicSubnet2
      RouteTableId: !Ref PublicRouteTable

  # --- Security Group ---
  WebServerSG:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Allow HTTP and SSH
      VpcId: !Ref VPC
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 80
          ToPort: 80
          CidrIp: 0.0.0.0/0
        - IpProtocol: tcp
          FromPort: 443
          ToPort: 443
          CidrIp: 0.0.0.0/0
        - IpProtocol: tcp
          FromPort: 22
          ToPort: 22
          CidrIp: 0.0.0.0/0
      Tags:
        - Key: Name
          Value: !Sub '${EnvironmentName}-WebSG'

  # --- EC2 Instance ---
  WebServer:
    Type: AWS::EC2::Instance
    Properties:
      InstanceType: !Ref InstanceType
      ImageId: !FindInMap [RegionMap, !Ref 'AWS::Region', AMI]
      KeyName: my-key-pair
      SubnetId: !Ref PublicSubnet1
      SecurityGroupIds:
        - !Ref WebServerSG
      UserData:
        Fn::Base64: |
          #!/bin/bash
          yum update -y
          yum install -y httpd
          systemctl start httpd
          systemctl enable httpd
          echo "<h1>Hello from CloudFormation!</h1>" > /var/www/html/index.html
      Tags:
        - Key: Name
          Value: !Sub '${EnvironmentName}-WebServer'

  # --- S3 Bucket ---
  AppBucket:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: !Sub '${EnvironmentName}-app-assets-${AWS::AccountId}'
      VersioningConfiguration:
        Status: Enabled
      LifecycleConfiguration:
        Rules:
          - Id: TransitionToIA
            Status: Enabled
            Transitions:
              - TransitionInDays: 90
                StorageClass: STANDARD_IA
      Tags:
        - Key: Environment
          Value: !Ref EnvironmentName

# ==============================
# MAPPINGS
# ==============================
Mappings:
  RegionMap:
    us-east-1:
      AMI: ami-0c55b159cbfafe1f0
    us-west-2:
      AMI: ami-0c55b159cbfafe1f0
    ap-southeast-1:
      AMI: ami-0c55b159cbfafe1f0

# ==============================
# OUTPUTS
# ==============================
Outputs:
  VPCId:
    Description: VPC ID
    Value: !Ref VPC
    Export:
      Name: !Sub '${AWS::StackName}-VPCId'

  WebServerPublicIP:
    Description: Public IP Web Server
    Value: !GetAtt WebServer.PublicIp

  WebsiteURL:
    Description: URL Website
    Value: !Sub 'http://${WebServer.PublicDnsName}'

  BucketName:
    Description: Nama S3 Bucket
    Value: !Ref AppBucket

Syntax Penting dalam YAML

Syntax Fungsi Contoh
!RefReferensi ke resource/parameter!Ref MyBucket
!GetAttGet attribute dari resource!GetAtt MyBucket.Arn
!SubString substitution!Sub '${AWS::StackName}-bucket'
!JoinJoin string!Join ['-', ['prefix', val]]
!SelectSelect index dari list!Select [0, !GetAZs '']
!FindInMapLookup dari mappings!FindInMap [Map, Key, Val]
!IfKondisional!If [Condition, TrueVal, FalseVal]
!ImportValueImport dari stack lain!ImportValue Shared-VPCId

4. Menulis Template JSON

JSON juga didukung penuh oleh CloudFormation. Meskipun lebih verbose dan sulit dibaca, JSON berguna untuk integrasi dengan tools yang menghasilkan JSON secara otomatis.

JSON
{
  "AWSTemplateFormatVersion": "2010-09-09",
  "Description": "S3 Bucket Template - JSON Format",

  "Parameters": {
    "BucketName": {
      "Type": "String",
      "Description": "Nama S3 Bucket",
      "MinLength": 3,
      "MaxLength": 63,
      "AllowedPattern": "[a-z0-9.-]*"
    },
    "Environment": {
      "Type": "String",
      "Default": "dev",
      "AllowedValues": ["dev", "staging", "production"]
    },
    "EnableVersioning": {
      "Type": "String",
      "Default": "true",
      "AllowedValues": ["true", "false"]
    }
  },

  "Resources": {
    "S3Bucket": {
      "Type": "AWS::S3::Bucket",
      "Properties": {
        "BucketName": {"Ref": "BucketName"},
        "VersioningConfiguration": {
          "Status": {
            "Fn::If": [
              "IsVersioningEnabled",
              "Enabled",
              "Suspended"
            ]
          }
        },
        "Tags": [
          {
            "Key": "Environment",
            "Value": {"Ref": "Environment"}
          },
          {
            "Key": "ManagedBy",
            "Value": "CloudFormation"
          }
        ]
      }
    },
    "BucketPolicy": {
      "Type": "AWS::S3::BucketPolicy",
      "Properties": {
        "Bucket": {"Ref": "S3Bucket"},
        "PolicyDocument": {
          "Version": "2012-10-17",
          "Statement": [
            {
              "Sid": "AllowSSLRequestsOnly",
              "Effect": "Deny",
              "Principal": "*",
              "Action": "s3:*",
              "Resource": {
                "Fn::Sub": "arn:aws:s3:::${S3Bucket}/*"
              },
              "Condition": {
                "Bool": {
                  "aws:SecureTransport": "false"
                }
              }
            }
          ]
        }
      }
    }
  },

  "Conditions": {
    "IsVersioningEnabled": {
      "Fn::Equals": [{"Ref": "EnableVersioning"}, "true"]
    }
  },

  "Outputs": {
    "BucketARN": {
      "Description": "ARN S3 Bucket",
      "Value": {"Fn::GetAtt": ["S3Bucket", "Arn"]}
    },
    "BucketName": {
      "Description": "Nama S3 Bucket",
      "Value": {"Ref": "S3Bucket"}
    }
  }
}
⚠️ Peringatan

Pastikan JSON valid sebelum deploy. Kesalahan syntax seperti koma yang hilang atau bracket yang tidak seimbang akan menyebabkan deploy gagal. Gunakan tool seperti cfn-lint atau aws cloudformation validate-template untuk validasi.

5. Intrinsic Functions

CloudFormation menyediakan berbagai intrinsic functions yang memungkinkan Anda melakukan manipulasi data langsung dalam template tanpa memerlukan script tambahan.

Fungsi Manipulasi String

YAML
# Sub β€” substitusi variabel
BucketName: !Sub
  - '${Env}-bucket-${AccountId}'
  - Env: !Ref EnvironmentName
    AccountId: !Ref 'AWS::AccountId'

# Join β€” gabungkan string
FullName: !Join
  - '-'
  - - !Ref Project
    - !Ref Environment
    - !Ref Component

# Select β€” pilih dari list
AZ: !Select [0, !GetAZs '']

# Split β€” pecah string
Subnets:
  Fn::Split:
    - ','
    - !Ref SubnetList

# Transform β€” ubah case
Upper: !Transform
  - String
  - !Ref Environment
  - ToUpper

Fungsi Lookup dan Referensi

YAML
# GetAtt β€” ambil attribute resource
Endpoint: !GetAtt MyLoadBalancer.DNSName
ARN: !GetAtt MyLambda.Arn

# FindInMap β€” lookup dari Mappings
AMI: !FindInMap
  - RegionMap
  - !Ref 'AWS::Region'
  - AMI

# ImportValue β€” import dari stack lain
VPCId: !ImportValue SharedStack-VPCId

# GetAZs β€” list availability zones
AZs: !GetAZs !Ref 'AWS::Region'

# Ref β€” referensi ke parameter atau pseudo-parameter
StackName: !Ref 'AWS::StackName'
Region: !Ref 'AWS::Region'
AccountId: !Ref 'AWS::AccountId'
NotificationARNs: !Ref 'AWS::NotificationARNs'

Fungsi Kondisional dan Logika

YAML
# If β€” kondisional
InstanceType: !If
  - IsProduction
  - m5.large
  - t3.micro

# Equals β€” perbandingan
Condition: !Equals [!Ref Env, 'production']

# And / Or / Not β€” logika boolean
ComplexCondition: !And
  - !Equals [!Ref Env, 'prod']
  - !Equals [!Ref Region, 'us-east-1']

# Contains β€” cek keanggotaan
IsUSRegion: !Contains
  - [us-east-1, us-west-2, us-east-2]
  - !Ref 'AWS::Region'

# EachMemberEquals β€” semua anggota sama
AllProdRegions: !EachMemberEquals
  - !Ref 'AWS::Region'
  - us-east-1

Fungsi Lainnya

YAML
# Base64 β€” encode base64
UserData: !Base64 |
  #!/bin/bash
  yum install -y httpd
  systemctl start httpd

# Cidr β€” hitung CIDR blocks
VPCCIDR: 10.0.0.0/16
Subnet1CIDR: !Select [0, !Cidr [!Ref VPCCIDR, 6, 8]]
Subnet2CIDR: !Select [1, !Cidr [!Ref VPCCIDR, 6, 8]]
# Output: 10.0.0.0/24, 10.0.1.0/24, dst.

# ImportValue
SharedVPC: !ImportValue Shared-VPC-ID

# Transform (macros)
Macros: !Transform
  - Name: MyMacro
  - { Input: 'some value' }
  - !Ref 'AWS::NoValue'

Pseudo Parameters

Pseudo Parameter Deskripsi Contoh Nilai
AWS::AccountIdID akun AWS123456789012
AWS::RegionRegion saat iniap-southeast-1
AWS::StackIdID stackarn:aws:cloudformation:...
AWS::StackNameNama stackmy-web-stack
AWS::URLSuffixDomain suffix AWSamazonaws.com
AWS::PartitionAWS partitionaws
AWS::NotificationARNsARN SNS untuk notifikasiarn:aws:sns:...

6. Membuat dan Mengelola Stack

Stack adalah kumpulan resource AWS yang dibuat dan dikelola sebagai satu unit. Setiap stack memiliki lifecycle sendiri β€” dari CREATE hingga DELETE β€” dan semua perubahan dikelola melalui update.

CLI Commands untuk Stack

Bash
# Validasi template
aws cloudformation validate-template \
  --template-body file://template.yaml

# Buat stack baru
aws cloudformation create-stack \
  --stack-name my-web-stack \
  --template-body file://template.yaml \
  --parameters ParameterKey=InstanceType,ParameterValue=t3.micro \
               ParameterKey=EnvironmentName,ParameterValue=dev \
  --capabilities CAPABILITY_IAM CAPABILITY_NAMED_IAM \
  --tags Key=Project,Value=MyApp Key=Environment,Value=Dev

# Cek status stack
aws cloudformation describe-stacks \
  --stack-name my-web-stack \
  --query 'Stacks[0].StackStatus'

# Output dari stack
aws cloudformation describe-stacks \
  --stack-name my-web-stack \
  --query 'Stacks[0].Outputs'

# Update stack
aws cloudformation update-stack \
  --stack-name my-web-stack \
  --template-body file://template-v2.yaml \
  --parameters ParameterKey=InstanceType,ParameterValue=t3.small

# Rollback manual jika update stuck
aws cloudformation continue-update-rollback \
  --stack-name my-web-stack

# Delete stack
aws cloudformation delete-stack \
  --stack-name my-web-stack

# Tunggu stack selesai
aws cloudformation wait stack-create-complete \
  --stack-name my-web-stack

# List semua stack
aws cloudformation list-stacks \
  --stack-status-filter CREATE_COMPLETE UPDATE_COMPLETE

Stack Events

Setiap perubahan pada stack mencatat event. Anda bisa melihat event untuk debugging dan monitoring.

Bash
# Lihat event stack
aws cloudformation describe-stack-events \
  --stack-name my-web-stack \
  --query 'StackEvents[*].[Timestamp,LogicalResourceId,ResourceStatus,ResourceStatusReason]' \
  --output table

# Output contoh:
# | 2026-06-26T10:00:00Z | WebServer        | CREATE_COMPLETE   | -                  |
# | 2026-06-26T09:58:00Z | WebServer        | CREATE_IN_PROGRESS| -                  |
# | 2026-06-26T09:57:00Z | WebSecurityGroup | CREATE_COMPLETE   | -                  |
# | 2026-06-26T09:55:00Z | VPC              | CREATE_COMPLETE   | -                  |
# | 2026-06-26T09:50:00Z | my-web-stack     | CREATE_IN_PROGRESS| -                  |

Stack Lifecycle States

Diagram: Stack Lifecycle
  CREATE_IN_PROGRESS ──► CREATE_COMPLETE
         β”‚                     β”‚
         β–Ό                     β–Ό
  CREATE_FAILED          UPDATE_IN_PROGRESS
         β”‚              β”Œβ”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”
         β–Ό              β–Ό               β–Ό
  DELETE_COMPLETE   UPDATE_COMPLETE  UPDATE_ROLLBACK
                      β”‚               IN_PROGRESS
                      β–Ό                    β”‚
                  DELETE_IN_PROGRESS        β–Ό
                                        UPDATE_ROLLBACK_
                                        COMPLETE

  Status lain:
  - REVIEW_IN_PROGRESS (untuk stack policy review)
  - IMPORT_IN_PROGRESS (untuk import resources)
  - IMPORT_ROLLBACK_IN_PROGRESS

7. Change Sets

Change Sets memungkinkan Anda melihat dampak dari perubahan yang akan dilakukan SEBELUM mengupdate stack. Ini seperti "dry run" yang sangat berguna untuk review perubahan infrastruktur sebelum diterapkan ke production.

Membuat dan Menggunakan Change Sets

Bash
# Buat change set dari template yang sudah ada
aws cloudformation create-change-set \
  --stack-name my-web-stack \
  --change-set-name add-rds-database \
  --template-body file://template-with-rds.yaml \
  --parameters ParameterKey=InstanceType,ParameterValue=t3.small \
  --capabilities CAPABILITY_IAM

# Cek status change set
aws cloudformation describe-change-set \
  --stack-name my-web-stack \
  --change-set-name add-rds-database

# Lihat detail perubahan
aws cloudformation describe-change-set \
  --stack-name my-web-stack \
  --change-set-name add-rds-database \
  --query 'Changes[*].[Type,ResourceChange.Action,ResourceChange.LogicalResourceId,ResourceChange.ResourceType]'

# Preview perubahan
# Action types:
#   Add    β†’ Resource baru akan dibuat
#   Modify β†’ Resource akan diubah
#   Remove β†’ Resource akan dihapus

# Execute change set (terapkan perubahan)
aws cloudformation execute-change-set \
  --stack-name my-web-stack \
  --change-set-name add-rds-database

# Hapus change set jika tidak jadi
aws cloudformation delete-change-set \
  --stack-name my-web-stack \
  --change-set-name add-rds-database

# List semua change sets
aws cloudformation list-change-sets \
  --stack-name my-web-stack

Contoh Output Change Set

Text
Change Set: add-rds-database
Stack: my-web-stack
Status: CREATE_COMPLETE

Changes:
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Type             β”‚ Action  β”‚ LogicalResourceId             β”‚ ResourceType         β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ Resource         β”‚ Add     β”‚ DatabaseSubnetGroup           β”‚ AWS::RDS::           β”‚
β”‚                  β”‚         β”‚                               β”‚ DBSubnetGroup        β”‚
β”‚ Resource         β”‚ Add     β”‚ RDSDatabase                   β”‚ AWS::RDS::           β”‚
β”‚                  β”‚         β”‚                               β”‚ DBInstance           β”‚
β”‚ Resource         β”‚ Modify  β”‚ WebServerSG                   β”‚ AWS::EC2::           β”‚
β”‚                  β”‚         β”‚                               β”‚ SecurityGroup        β”‚
β”‚ Resource         β”‚ Add     β”‚ DatabaseSecurityGroup          β”‚ AWS::EC2::           β”‚
β”‚                  β”‚         β”‚                               β”‚ SecurityGroup        β”‚
β”‚ Resource         β”‚ Add     β”‚ DBSubnet1                     β”‚ AWS::EC2::Subnet    β”‚
β”‚ Resource         β”‚ Add     β”‚ DBSubnet2                     β”‚ AWS::EC2::Subnet    β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
πŸ’‘ Tips

Gunakan --no-execute-change-set saat membuat change set agar tidak langsung dijalankan. Review perubahan terlebih dahulu, lalu execute secara manual jika sudah yakin. Ini adalah practice yang sangat disarankan untuk environment production.

8. Stack Sets untuk Multi-Region

Stack Sets memungkinkan Anda deploy CloudFormation stack ke beberapa akun AWS dan/atau region secara bersamaan dari satu template. Ini sangat berguna untuk organisasi yang menjalankan infrastruktur di banyak region atau akun.

Konsep Stack Sets

Diagram: Stack Sets Architecture
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                   STACK SET                                   β”‚
β”‚              "SharedSecurityPolicies"                         β”‚
β”‚                                                               β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚
β”‚  β”‚  Admin Account (Management Account)                   β”‚   β”‚
β”‚  β”‚  - Mengelola template                                 β”‚   β”‚
β”‚  β”‚  - Mengatur deployment targets                         β”‚   β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
β”‚                          β”‚                                   β”‚
β”‚         β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                  β”‚
β”‚         β–Ό                β–Ό                β–Ό                  β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”          β”‚
β”‚  β”‚ Account A  β”‚   β”‚ Account B  β”‚   β”‚ Account C  β”‚          β”‚
β”‚  β”‚ us-east-1  β”‚   β”‚ us-east-1  β”‚   β”‚ eu-west-1  β”‚          β”‚
β”‚  β”‚            β”‚   β”‚ us-west-2  β”‚   β”‚            β”‚          β”‚
β”‚  β”‚ Stack:     β”‚   β”‚ Stack:     β”‚   β”‚ Stack:     β”‚          β”‚
β”‚  β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β” β”‚   β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β” β”‚   β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β” β”‚          β”‚
β”‚  β”‚ β”‚IAM Roleβ”‚ β”‚   β”‚ β”‚IAM Roleβ”‚ β”‚   β”‚ β”‚IAM Roleβ”‚ β”‚          β”‚
β”‚  β”‚ β”‚SG      β”‚ β”‚   β”‚ β”‚SG      β”‚ β”‚   β”‚ β”‚SG      β”‚ β”‚          β”‚
β”‚  β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚   β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚   β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚          β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜          β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Commands untuk Stack Sets

Bash
# Buat Stack Set
aws cloudformation create-stack-set \
  --stack-set-name SharedSecurityPolicies \
  --template-body file://shared-policies.yaml \
  --description "Security policies untuk semua akun" \
  --permission-model SELF_MANAGED \
  --administration-role-arn arn:aws:iam::123456789012:role/AWSCloudFormationStackSetAdministrationRole \
  --execution-role-name AWSCloudFormationStackSetExecutionRole

# Deploy ke target accounts/regions
aws cloudformation create-instances-from-stack-set \
  --stack-set-name SharedSecurityPolicies \
  --accounts '["111111111111","222222222222"]' \
  --regions '["us-east-1","us-west-2","eu-west-1"]'

# Cek status deployment
aws cloudformation list-stack-instances \
  --stack-set-name SharedSecurityPolicies

# Update Stack Set
aws cloudformation update-stack-set \
  --stack-set-name SharedSecurityPolicies \
  --template-body file://shared-policies-v2.yaml \
  --accounts '["111111111111","222222222222"]' \
  --regions '["us-east-1","us-west-2"]'

# Hapus Stack Set
aws cloudformation delete-stack-instances \
  --stack-set-name SharedSecurityPolicies \
  --accounts '["111111111111"]' \
  --regions '["us-east-1"]' \
  --retain-resources NONE

aws cloudformation delete-stack-set \
  --stack-set-name SharedSecurityPolicies

9. Drift Detection

Drift Detection adalah fitur CloudFormation yang mendeteksi apakah resource dalam stack telah diubah secara manual di luar CloudFormation. Perubahan manual ini disebut "drift" dan bisa menyebabkan ketidaksesuaian antara state aktual infrastruktur dengan state yang tercatat di CloudFormation.

Mengapa Drift Detection Penting?

Aspek Penjelasan
KonsistensiPastikan infrastruktur sesuai template, bukan diubah manual
SecurityDetect perubahan keamanan yang tidak diinginkan (misalnya SG dibuka)
ComplianceMemastikan infrastruktur comply dengan standar organisasi
TroubleshootingMencari tahu mengapa stack gagal update karena perubahan manual
AuditMelacak siapa yang mengubah infrastruktur di luar prosedur

Menjalankan Drift Detection

Bash
# Mulai drift detection
aws cloudformation detect-stack-resource-drift \
  --stack-name my-web-stack

# Atau deteksi drift untuk semua resource dalam stack
DRIFT_DETECTION_ID=$(aws cloudformation detect-stack-drift \
  --stack-name my-web-stack \
  --query 'StackDriftDetectionId' \
  --output text)

echo "Detection ID: $DRIFT_DETECTION_ID"

# Cek status drift detection
aws cloudformation describe-stack-drift-detection-status \
  --stack-drift-detection-id $DRIFT_DETECTION_ID

# Lihat detail drift
aws cloudformation describe-stack-resource-drifts \
  --stack-name my-web-stack \
  --stack-resource-drift-status-filters MODIFIED DELETED

# Output detail drift:
# StackResourceDrift:
#   - LogicalResourceId: WebSecurityGroup
#     ResourceType: AWS::EC2::SecurityGroup
#     StackResourceDriftStatus: MODIFIED
#     ActualProperties: '{"SecurityGroupIngress": [...443, 80, 22...]}'
#     ExpectedProperties: '{"SecurityGroupIngress": [...80, 443, 22...]}'
#     PropertyDifferences:
#       - PropertyPath: "/SecurityGroupIngress/1/FromPort"
#         ExpectedValue: "443"
#         ActualValue: "8443"
#         DifferenceType: NOT_EQUAL

# Batch detect untuk semua stack
aws cloudformation list-stack-resource-drifts \
  --stack-name my-web-stack \
  --stack-resource-drift-status-filters MODIFIED

Perbedaan Resource Status

Status Arti
IN_SYNCResource sesuai dengan template β€” tidak ada drift
MODIFIEDResource telah diubah secara manual di luar CloudFormation
DELETEDResource telah dihapus secara manual di luar CloudFormation

Auto-Remediation untuk Drift

Bash
# Option 1: Update stack untuk sync kembali ke template
# CloudFormation akan mengembalikan resource ke state template
aws cloudformation update-stack \
  --stack-name my-web-stack \
  --use-previous-template

# Option 2: Import resource yang di-drift
# Jika ingin mengadopsi perubahan manual ke template
aws cloudformation import-resources-to-stack \
  --stack-name my-web-stack \
  --resource-identifiers \
    'ResourceType=AWS::EC2::SecurityGroup,LogicalResourceId=WebServerSG,ResourceIdentifier=sg-0123456789abcdef'

# Option 3: Auto-remediation dengan EventBridge
# Trigger drift detection otomatis via schedule
aws events put-rule \
  --name "MonthlyDriftDetection" \
  --schedule-expression "cron(0 1 1 * ? *)" \
  --description "Deteksi drift bulanan"

# Script remediation otomatis
cat << 'EOF' > remediate-drift.sh
#!/bin/bash
STACK_NAME=$1
DRIFT_ID=$(aws cloudformation detect-stack-drift \
  --stack-name $STACK_NAME \
  --query 'StackDriftDetectionId' --output text)

aws cloudformation wait stack-drift-detection-complete \
  --stack-drift-detection-id $DRIFT_ID

DRIFT_STATUS=$(aws cloudformation describe-stack-drift-detection-status \
  --stack-drift-detection-id $DRIFT_ID \
  --query 'DetectionStatus' --output text)

if [ "$DRIFT_STATUS" = "DETECTION_COMPLETE" ]; then
  MODIFIED=$(aws cloudformation describe-stack-resource-drifts \
    --stack-name $STACK_NAME \
    --stack-resource-drift-status-filters MODIFIED \
    --query 'length(StackResourceDrifts)' --output text)

  if [ "$MODIFIED" -gt "0" ]; then
    echo "Drift detected! Resources modified: $MODIFIED"
    aws cloudformation update-stack \
      --stack-name $STACK_NAME \
      --use-previous-template
  fi
fi
EOF
⚠️ Peringatan

Tidak semua resource type didukung oleh drift detection. Beberapa properti juga tidak bisa di-detect. Selalu cek dokumentasi AWS untuk resource type yang ingin Anda monitor. Drift detection tersedia gratis β€” Anda hanya membayar untuk API calls.

10. Nested Stacks

Nested Stacks memungkinkan Anda membuat stack dari stack lain β€” template induk (parent) mereferensikan child stack melalui AWS::CloudFormation::Stack. Ini membantu memecah template besar menjadi modul-modul yang lebih kecil dan reusible.

Konsep Nested Stacks

Diagram: Nested Stack Structure
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚              PARENT STACK: MainInfrastructure          β”‚
β”‚                                                        β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
β”‚  β”‚  Resources:                                      β”‚  β”‚
β”‚  β”‚    VPCStack:                                      β”‚  β”‚
β”‚  β”‚      Type: AWS::CloudFormation::Stack            β”‚  β”‚
β”‚  β”‚      TemplateURL: templates/vpc.yaml              β”‚  β”‚
β”‚  β”‚                                                   β”‚  β”‚
β”‚  β”‚    ComputeStack:                                  β”‚  β”‚
β”‚  β”‚      Type: AWS::CloudFormation::Stack            β”‚  β”‚
β”‚  β”‚      TemplateURL: templates/compute.yaml          β”‚  β”‚
β”‚  β”‚      Parameters:                                  β”‚  β”‚
β”‚  β”‚        VPCId: !GetAtt VPCStack.Outputs.VPCId     β”‚  β”‚
β”‚  β”‚                                                   β”‚  β”‚
β”‚  β”‚    DatabaseStack:                                 β”‚  β”‚
β”‚  β”‚      Type: AWS::CloudFormation::Stack            β”‚  β”‚
β”‚  β”‚      TemplateURL: templates/database.yaml         β”‚  β”‚
β”‚  β”‚      Parameters:                                  β”‚  β”‚
β”‚  β”‚        VPCId: !GetAtt VPCStack.Outputs.VPCId     β”‚  β”‚
β”‚  β”‚                                                   β”‚  β”‚
β”‚  β”‚    MonitoringStack:                               β”‚  β”‚
β”‚  β”‚      Type: AWS::CloudFormation::Stack            β”‚  β”‚
β”‚  β”‚      TemplateURL: templates/monitoring.yaml       β”‚  β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
β”‚                                                        β”‚
β”‚  Child Stacks:                                        β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”β”‚
β”‚  β”‚   VPC    β”‚ β”‚ Compute  β”‚ β”‚ Database β”‚ β”‚Monitoringβ”‚β”‚
β”‚  β”‚  Stack   β”‚ β”‚  Stack   β”‚ β”‚  Stack   β”‚ β”‚  Stack   β”‚β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
YAML
# Template Parent (main.yaml)
AWSTemplateFormatVersion: '2010-09-09'
Description: Main Infrastructure Stack

Parameters:
  EnvironmentName:
    Type: String
    Default: dev

Resources:
  VPCStack:
    Type: AWS::CloudFormation::Stack
    Properties:
      TemplateURL: https://s3.amazonaws.com/my-templates/vpc.yaml
      Parameters:
        VpcCIDR: 10.0.0.0/16
        EnvironmentName: !Ref EnvironmentName

  ComputeStack:
    Type: AWS::CloudFormation::Stack
    DependsOn: VPCStack
    Properties:
      TemplateURL: https://s3.amazonaws.com/my-templates/compute.yaml
      Parameters:
        VPCId: !GetAtt VPCStack.Outputs.VPCId
        SubnetId: !GetAtt VPCStack.Outputs.PublicSubnet1
        InstanceType: t3.micro

  DatabaseStack:
    Type: AWS::CloudFormation::Stack
    DependsOn: VPCStack
    Properties:
      TemplateURL: https://s3.amazonaws.com/my-templates/database.yaml
      Parameters:
        VPCId: !GetAtt VPCStack.Outputs.VPCId
        SubnetIds: !GetAtt VPCStack.Outputs.PrivateSubnetIds
        DBPassword: !Ref DBPassword

Outputs:
  VPCId:
    Value: !GetAtt VPCStack.Outputs.VPCId
  WebServerIP:
    Value: !GetAtt ComputeStack.Outputs.PublicIP
  DatabaseEndpoint:
    Value: !GetAtt DatabaseStack.Outputs.Endpoint

11. Best Practices

Struktur Template

Best Practice Penjelasan
Pisahkan environmentGunakan parameter untuk mengontrol perbedaan dev/staging/prod
Gunakan nested stacksPecah template besar menjadi modul-modul kecil
Parameterize nilaiJangan hardcode β€” gunakan parameters untuk values yang berubah
Output semua valuesExport values penting untuk digunakan stack lain
Gunakan mappingsUntuk region-specific values seperti AMI IDs
Description yang jelasTulis description yang informatif untuk setiap template

Keamanan

YAML
# 1. Gunakan NoEcho untuk secrets
DBPassword:
  Type: String
  NoEcho: true
  MinLength: 12

# 2. Gunakan AWS Secrets Manager
DBSecret:
  Type: AWS::SecretsManager::Secret
  Properties:
    Name: !Sub '${EnvironmentName}/database/credentials'
    GenerateSecretString:
      SecretStringTemplate: '{"username": "admin"}'
      GenerateStringKey: password
      PasswordLength: 32
      ExcludeCharacters: '"@/\'

# 3. Gunakan KMS untuk encryption
EncryptedBucket:
  Type: AWS::S3::Bucket
  Properties:
    BucketEncryption:
      ServerSideEncryptionConfiguration:
        - ServerSideEncryptionByDefault:
            SSEAlgorithm: aws:kms
            KMSMasterKeyID: !Ref KMSKey

# 4. VPC Endpoints untuk akses private
VPCEndpoint:
  Type: AWS::EC2::VPCEndpoint
  Properties:
    ServiceName: !Sub 'com.amazonaws.${AWS::Region}.s3'
    VpcId: !Ref VPC
    RouteTableIds:
      - !Ref PrivateRouteTable

# 5. Stack policy untuk proteksi resource kritis
# Simpan sebagai stack-policy.json:
# {
#   "Statement": [
#     {
#       "Effect": "Deny",
#       "Action": "Update:*",
#       "Principal": "*",
#       "Resource": "LogicalResourceId/DatabaseInstance",
#       "Condition": {
#         "StringEquals": {"ResourceType": "AWS::RDS::DBInstance"}
#       }
#     }
#   ]
# }

Organisasi dan Naming

Text
Rekomendasi Struktur Project:
β”œβ”€β”€ templates/
β”‚   β”œβ”€β”€ main.yaml              # Parent stack
β”‚   β”œβ”€β”€ vpc.yaml               # VPC resources
β”‚   β”œβ”€β”€ compute.yaml           # EC2, ECS, Lambda
β”‚   β”œβ”€β”€ database.yaml          # RDS, DynamoDB
β”‚   β”œβ”€β”€ monitoring.yaml        # CloudWatch, SNS
β”‚   └── security.yaml          # IAM, KMS, Secrets
β”œβ”€β”€ parameters/
β”‚   β”œβ”€β”€ dev.json               # Parameters untuk dev
β”‚   β”œβ”€β”€ staging.json           # Parameters untuk staging
β”‚   └── production.json        # Parameters untuk production
β”œβ”€β”€ scripts/
β”‚   β”œβ”€β”€ deploy.sh              # Deployment script
β”‚   β”œβ”€β”€ validate.sh            # Template validation
β”‚   └── drift-check.sh         # Drift detection
β”œβ”€β”€ README.md
└── .github/
    └── workflows/
        └── deploy.yml         # CI/CD pipeline

CI/CD dengan CloudFormation

YAML
# GitHub Actions: Deploy CloudFormation
name: Deploy CloudFormation
on:
  push:
    branches: [main]
    paths:
      - 'templates/**'

jobs:
  validate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Validate templates
        run: |
          for f in templates/*.yaml; do
            echo "Validating $f..."
            aws cloudformation validate-template \
              --template-body file://$f
          done

  deploy:
    needs: validate
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Deploy to Dev
        run: |
          aws cloudformation deploy \
            --template-file templates/main.yaml \
            --stack-name my-app-dev \
            --parameter-overrides file://parameters/dev.json \
            --capabilities CAPABILITY_IAM \
            --tags Environment=Dev Project=MyApp

      - name: Run Tests
        run: |
          # Test endpoints
          curl -f http://$(aws cloudformation describe-stacks \
            --stack-name my-app-dev \
            --query 'Stacks[0].Outputs[?OutputKey==`WebServerIP`].OutputValue' \
            --output text)/health

      - name: Deploy to Production
        if: github.ref == 'refs/heads/main'
        run: |
          aws cloudformation deploy \
            --template-file templates/main.yaml \
            --stack-name my-app-production \
            --parameter-overrides file://parameters/production.json \
            --capabilities CAPABILITY_IAM \
            --no-fail-on-empty-changeset \
            --tags Environment=Production Project=MyApp

Cost Optimization Tips

Tip Detail
Gunakan CAPABILITY_AUTO_EXPANDUntuk macro dan transforms β€” hati-hati karena bisa mengubah resource secara tidak terduga
Set termination protectionHindari stack production terhapus secara tidak sengaja
Gunakan change setsSelalu review perubahan sebelum execute
Monitoring driftJalankan drift detection secara berkala (weekly/monthly)
Tag semua resourcesMemudahkan tracking cost per project/team
Cleanup unused stacksHapus stack yang sudah tidak digunakan untuk menghemat cost
πŸ’‘ Tips

Gunakan CloudFormation Designer (tersedia di AWS Console) untuk visualisasi template secara grafis. Sangat membantu untuk memahami dependency antar resource, terutama untuk template yang kompleks dengan banyak resource.

12. Quiz Pemahaman

πŸ“ Quiz: Pemahaman CloudFormation

1. Komponen manakah yang WAJIB ada dalam CloudFormation template?

2. Apa fungsi !Ref dalam CloudFormation?

3. Fitur apa yang mendeteksi perubahan manual pada resource AWS?

4. Untuk apa NoEcho: true digunakan pada parameter?

5. Apa keuntungan utama Change Sets?

πŸ” Zoom
100%
🎨 Tema