본문 바로가기

KOSA 클라우드 솔루션즈 아키텍트 양성과정

[7.4] Terraform (AWS : ASG, VPC, EC2, ALB )(AZURE)

[AWS]

- ec2 asg -

# vi variables.tf

variable "instance_security_group_name" {
  description = "The name of the security group for the EC2 Instances"
  type        = string
  default     = "terraform-example-instance"
}

variable "http_port" {
  description = "The port the server will use for HTTP requests"
  type        = number
  default     = 80
}

variable "ssh_port" {
  description = "The port the server will use for SSH requests"
  type        = number
  default     = 22
}

variable "alb_name" {
  description = "The name of the ALB"
  type        = string
  default     = "terraform-asg-example"
}

variable "alb_security_group_name" {
  description = "The name of the security group for the ALB"
  type        = string
  default     = "terraform-example-alb"
}

# vi main.tf

provider "aws" {                 => provider 정보는 main 파일에만 넣어주기, provider 정보가 중복되면 애러 발생
  region = "ap-northeast-2"
}

 

 - new-vpc -

resource "aws_vpc" "new_vpc" { 
=> 자원의 이름(Terraform이 지정한 이름  : "aws_vpc", 내가 정하는 자원의 이름 : "new_vpc")
  cidr_block  = "192.168.0.0/16"
  enable_dns_hostnames = true
  enable_dns_support = true
enable_dns_support = true : resolver를 설정하는 코드, 루트 DNS와 트래픽 송수신

  instance_tenancy = "default"

  tags = {
    Name = "NEW-VPC"
  }
}

data "aws_availability_zones" "available" {
=> data : aws가 이미 가지고 있는 정보를 가져올 때 data 사용
  state = "available"
}

resource "aws_subnet" "new_public_subnet_2a" {
  vpc_id = aws_vpc.new_vpc.id
  cidr_block = "192.168.0.0/20"
  map_public_ip_on_launch = true
=> 퍼블릭 IP 활성화 : default는 false
  availability_zone = data.aws_availability_zones.available.names[0]
=> names : ap-northeast-2a = [0], 2b = [1], 2c = [2], 2d = [3]와 같은 AZ의 이름
  tags = {
    Name = "NEW-PUBLIC-SUBNET-2A"
=> defalut 서브넷과 구분하기 위해 tag 달기
  }
}

resource "aws_subnet" "new_public_subnet_2b" {
  vpc_id = aws_vpc.new_vpc.id
  cidr_block = "192.168.16.0/20"
  map_public_ip_on_launch = true
  availability_zone = data.aws_availability_zones.available.names[1]
  tags = {
    Name = "NEW-PUBLIC-SUBNET-2B"
  }
}

resource "aws_subnet" "new_public_subnet_2c" {
  vpc_id = aws_vpc.new_vpc.id
  cidr_block = "192.168.32.0/20"
  map_public_ip_on_launch = true
  availability_zone = data.aws_availability_zones.available.names[2]
  tags = {
    Name = "NEW-PUBLIC-SUBNET-2C"
  }
}

resource "aws_subnet" "new_public_subnet_2d" {
  vpc_id = aws_vpc.new_vpc.id
  cidr_block = "192.168.48.0/20"
  map_public_ip_on_launch = true
  availability_zone = data.aws_availability_zones.available.names[3]
  tags = {
    Name = "NEW-PUBLIC-SUBNET-2D"
  }
}

resource "aws_internet_gateway" "new_igw" {
  vpc_id = aws_vpc.new_vpc.id
=> vpc_id를 삽입함으로써 IGW의 생성과 동시에 VPC와 attach됨
  tags = {
    Name = "NEW-IGW"
  }
}

resource "aws_route_table" "new_public_rtb" {
=> new_public_rtb : new와 같은 부분을 접두어(prefix)라고 함, 접두어를 붙이면 다른 서비와 구분 용이
  vpc_id = aws_vpc.new_vpc.id

  route {
    cidr_block = "0.0.0.0/0"
=> 모든 IP를 하단의 gateway_id(넥스트 홉)으로 보냄
    gateway_id = aws_internet_gateway.new_igw.id
  }
  tags = {
    Name = "NEW-PUBLIC-RTB"
  }
}

resource "aws_route_table_association" "new_public_subnet_2a_association" {
  subnet_id = aws_subnet.new_public_subnet_2a.id
  route_table_id = aws_route_table.new_public_rtb.id
}

resource "aws_route_table_association" "new_public_subnet_2b_association" {
  subnet_id = aws_subnet.new_public_subnet_2b.id
  route_table_id = aws_route_table.new_public_rtb.id
}

resource "aws_route_table_association" "new_public_subnet_2c_association" {
  subnet_id = aws_subnet.new_public_subnet_2c.id
  route_table_id = aws_route_table.new_public_rtb.id
}

resource "aws_route_table_association" "new_public_subnet_2d_association" {
  subnet_id = aws_subnet.new_public_subnet_2d.id
  route_table_id = aws_route_table.new_public_rtb.id
}  => 명시적 연결(퍼블릭 라우팅 테이블을 각각의 서브넷에 association(맵핑))

 

### asg ###

resource "aws_security_group" "instance" {
  name   = var.instance_security_group_name
=> 여기서 name은 tag와 구분됨
=> var : 어딘가에 variable을 정의 해놓고 그 값을 활용함(variable은 해당 라인의 전, 후 어디 있어도 관계 없음)
=> instance_security_group_name : 변수명(해당 변수값에 정의된 default 정보를 가져오게 됨)
  vpc_id = aws_vpc.new_vpc.id
=> 전에 만들어 놓은 vpc의 id를 참조함

  ingress {
    from_port   = var.http_port
    to_port     = var.http_port
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }
  ingress {
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = ["112.221.225.165 /32", "192.168.0.1/32"]
=> 만약, 출발지 IP를 특정한 IP로 설정하면 해당 IP가 아니면 접속이 되지 않으니 주의
  }
  ingress {
    from_port   = -1
    to_port     = -1
    protocol    = "icmp"
    cidr_blocks = ["0.0.0.0/0"]
  }
  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

resource "aws_launch_configuration" "example" {
=> 실습 시에 시작 템플릿(lunch template)을 ASG에 적용 시켰음, 여기서는 시작 구성(launch configuration)이용
=> 시작 구성은 버전 관리 불가능
  image_id        = "ami-0fd0765afb77bcca7"
  instance_type   = "t2.micro"
  security_groups = [aws_security_group.instance.id]
  key_name        = "new-key"
  user_data       = file("user-data.sh")
=> user-data.sh 파일을 만들어 놓고, 해당 파일의 정보를 읽어서 사용
user-data.sh 파일 : ec2를 만든 시간을 echo로 넣는 것(로드밸런싱 되고 있는 것을 확인하기 위함), sleep으로 10후 부하

  # Required when using a launch configuration with an auto scaling group.
  lifecycle {
    create_before_destroy = true
=> 만들어진 후에 제거해라
  }
}

resource "aws_autoscaling_group" "example" {
  launch_configuration = aws_launch_configuration.example.name
  vpc_zone_identifier  = [  => vpc존의 식별자(identifier)는 가용 영역을 의미
    aws_subnet.new_public_subnet_2a.id,
    aws_subnet.new_public_subnet_2b.id,
    aws_subnet.new_public_subnet_2c.id,
    aws_subnet.new_public_subnet_2d.id
  ]

  target_group_arns = [aws_lb_target_group.asg.arn]
=> 로드 밸런서를 이용할 것이기에 타깃 그룹도 설정
=> arn(amazon resource number) : 타깃 그룹 자원의 넘버
  health_check_type = "ELB"
=> 원래 ALB가 타깃 그룹의 EC2 헬스 체크(단순히 서버가 다운 되지 않았는지 확인)
=> 거기에 더해 ASG가 ELB에 헬스체크(메모리나 자원의 부족 확인 가능)

  min_size              = 2 
=> 아무리 사용량이 없어도, 단일 장애 지점(SPOF)을 회피하기 위해 2개 이상의 서버를 유지
=> SPOF : 동작 하지 않으면 전체 시스템이 중단되게 만드는 요소
=> min_size는 desired_capacity 이하의 사이즈로 셋팅해야 함
  desired_capacity = 2
  max_size             = 4

  tag {
    key                 = "Name"
    value               = "terraform-asg-example"
    propagate_at_launch = true
=> true :  terraform-asg-example이라는 태그의 이름을 늘어 나는 ec2에서도 사용하겠다는 의미
=> false : 나중에 생기는 ec2는 태그가 없는 상태로 생성됨
  }
}

resource "aws_lb" "example" {

  name               = var.alb_name

  load_balancer_type = "application"
=> ALB를 만들 것이라서 type을 application으로 지정, NLB를 만들 것이라면 network로 지정
  subnets            = [
    aws_subnet.new_public_subnet_2a.id,
    aws_subnet.new_public_subnet_2b.id,
    aws_subnet.new_public_subnet_2c.id,
    aws_subnet.new_public_subnet_2d.id
  ] => 로드 밸런서 밑에 넣어 줄 서브넷들, 실습에서는 t2.micro를 사용해서 2a와 2c에만 인스턴스가 생성될 것
  security_groups    = [aws_security_group.alb.id]
}

resource "aws_lb_listener" "http" {
  load_balancer_arn = aws_lb.example.arn
  port              = var.http_port
  protocol          = "HTTP"

  # By default, return a simple 404 page
  default_action {
    type = "fixed-response"

    fixed_response {
      content_type = "text/plain"
      message_body = "404: page not found"
      status_code  = 404
    }
  }
}

resource "aws_lb_target_group" "asg" {

  name = var.alb_name

  port     = var.http_port
  protocol = "HTTP"
  vpc_id   = aws_vpc.new_vpc.id

  health_check {
    path                = "/"
    protocol            = "HTTP"
    matcher             = "200"
    interval            = 15
    timeout             = 3
    healthy_threshold   = 2
    unhealthy_threshold = 2
  }
}

resource "aws_lb_listener_rule" "asg" {
  listener_arn = aws_lb_listener.http.arn
  priority     = 100

  condition {
    path_pattern {
      values = ["*"]
    }
  }
=> 헤더의 역할 :  현재 설정된 규칙은 모든 헤더를 받아서 넘겨주도록 지정됨

  action {
    type             = "forward"
    target_group_arn = aws_lb_target_group.asg.arn
  }
}

resource "aws_security_group" "alb" {
  vpc_id = aws_vpc.new_vpc.id
  name   = var.alb_security_group_name

  # Allow inbound HTTP requests
  ingress {
    from_port   = var.http_port
    to_port     = var.http_port
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }
ingress {
    from_port   = 443
    to_port     = 443
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  # Allow all outbound requests
  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
}
resource "aws_autoscaling_policy" "scale_in" {
  name                   = "ScaleInPolicy"
  autoscaling_group_name = aws_autoscaling_group.example.name
  adjustment_type        = "ChangeInCapacity"
=> desired capacity 수의 변화, 스케일링 될 때 우선 desired capacity가 변화하고 인스턴스가 늘어나거나 줄어듦
  scaling_adjustment     = -1
=> 인스턴스를 한개 지우겠다는 의미
  cooldown               = 300
=> cooldown : 연속적으로 스케일링 되어야 하는 상황에서 중간에 간격을 두어 오동작을 방지
}

resource "aws_cloudwatch_metric_alarm" "scale_in" { => 클라우드 와치의 알람이 scale in의 트리거 역할
  alarm_description   = "Monitors CPU utilization for Terramino ASG"
  alarm_actions       = [aws_autoscaling_policy.scale_in.arn]
=> arn :  액션 부분인 오토 스케일링 policy와 트리거인 클라우드 와치의 알람을 연결
  alarm_name          = "ScaleInAlarm"
  comparison_operator = "LessThanOrEqualToThreshold"
=> CPU 사용률 임계값인 30% 이하
  namespace           = "AWS/EC2"
=> 지표에서 EC2 인스턴스를 대상으로 측정
  metric_name         = "CPUUtilization"
  threshold           = "30"
  evaluation_periods  = "1"
=> 단 한 번이라도
  period              = "300"
=> 5분 마다 한 번씩 측정치를 체크함
  statistic           = "Average"
=> 5분 동안의 평균
=> 단 한 번이라도 5분 평균 값이 30% 이하로 떨어지면, scale in 알람 발생

  dimensions = {
    AutoScalingGroupName = aws_autoscaling_group.example.name
  }
=> dimensions : 알람을 오토스케일링 그룹 내에 있는 오토 스케일링 policy와 연관해서 사용할 것
=> cf.) scale in을 scale down이라고 표현하기도 함, 원래 scale down은 scale up의 반대 개념이지만 scale in의 반대로도 쓰임. 또한 scale out을 scale up으로 표현하기도 함.( = 수직적, 수평적 확장 축소와 관계 없이 용어를 사용하기도 함)
테라폼 레지스트리에서도 sacle in을 scale down으로 표현하고, scale out을 scale up으로 표현함
}
resource "aws_autoscaling_policy" "scale_out" {
  name                   = "ScaleOutPolicy"
  autoscaling_group_name = aws_autoscaling_group.example.name
  adjustment_type        = "ChangeInCapacity"
  scaling_adjustment     = 1
  cooldown               = 300
}

resource "aws_cloudwatch_metric_alarm" "scale_out" {
  alarm_description   = "Monitors CPU utilization for Terramino ASG"
  alarm_actions       = [aws_autoscaling_policy.scale_out.arn]
  alarm_name          = "ScaleOutAlarm"
  comparison_operator = "GreaterThanOrEqualToThreshold"
  namespace           = "AWS/EC2"
  metric_name         = "CPUUtilization"
  threshold           = "70"
  evaluation_periods  = "1"
  period              = "300"
  statistic           = "Average"

  dimensions = {
    AutoScalingGroupName = aws_autoscaling_group.example.name
  }
}

# vi outputs.tf                   => 파일의 이름은 임의로 지정 가능
output "alb_dns_name" { => 출력될 이름 alb_dns_name 역시 임의로 정해도 됨
  value       = aws_lb.example.dns_name
  description = "The domain name of the load balancer"
}

# terraform init
# terraform validate
# terraform plan
# terraform apply
# terraform output alb_dns_name

--- aws_set ---

# git clone https://github.com/hali-linux/aws_set.git => aws_set 폴더를 통채로 내려 받음
# variables.tf

variable "security_group_name" {
  description = "The name of the security group"
  type        = string
  default     = "terraform-example-instance"
}

# vi main.tf
provider "aws" {
  region = "ap-northeast-2"
}

### vpc start ###

resource "aws_vpc" "new_vpc" {
  cidr_block  = "192.168.0.0/16"
  enable_dns_hostnames = true
  enable_dns_support = true
  instance_tenancy = "default"

  tags = {
    Name = "NEW-VPC"
  }
}

data "aws_availability_zones" "available" {
  state = "available"
}

resource "aws_subnet" "new_public_subnet_2a" {
  vpc_id = aws_vpc.new_vpc.id
  cidr_block = "192.168.0.0/20"
  map_public_ip_on_launch = true
  availability_zone = data.aws_availability_zones.available.names[0]
  tags = {
    Name = "NEW-PUBLIC-SUBNET-2A"
  }
}

resource "aws_subnet" "new_public_subnet_2b" {
  vpc_id = aws_vpc.new_vpc.id
  cidr_block = "192.168.16.0/20"
  map_public_ip_on_launch = true
  availability_zone = data.aws_availability_zones.available.names[1]
  tags = {
    Name = "NEW-PUBLIC-SUBNET-2B"
  }
}

resource "aws_subnet" "new_public_subnet_2c" {
  vpc_id = aws_vpc.new_vpc.id
  cidr_block = "192.168.32.0/20"
  map_public_ip_on_launch = true
  availability_zone = data.aws_availability_zones.available.names[2]
  tags = {
    Name = "NEW-PUBLIC-SUBNET-2C"
  }
}

resource "aws_subnet" "new_public_subnet_2d" {
  vpc_id = aws_vpc.new_vpc.id
  cidr_block = "192.168.48.0/20"
  map_public_ip_on_launch = true
  availability_zone = data.aws_availability_zones.available.names[3]
  tags = {
    Name = "NEW-PUBLIC-SUBNET-2D"
  }
}

resource "aws_internet_gateway" "new_igw" {
  vpc_id = aws_vpc.new_vpc.id
  tags = {
    Name = "NEW-IGW"
  }
}

resource "aws_route_table" "new_public_rtb" {
  vpc_id = aws_vpc.new_vpc.id

  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_internet_gateway.new_igw.id
  }
  tags = {
    Name = "NEW-PUBLIC-RTB"
  }
}

resource "aws_route_table_association" "new_public_subnet_2a_association" {
  subnet_id = aws_subnet.new_public_subnet_2a.id
  route_table_id = aws_route_table.new_public_rtb.id
}

resource "aws_route_table_association" "new_public_subnet_2b_association" {
  subnet_id = aws_subnet.new_public_subnet_2b.id
  route_table_id = aws_route_table.new_public_rtb.id
}

resource "aws_route_table_association" "new_public_subnet_2c_association" {
  subnet_id = aws_subnet.new_public_subnet_2c.id
  route_table_id = aws_route_table.new_public_rtb.id
}

resource "aws_route_table_association" "new_public_subnet_2d_association" {
  subnet_id = aws_subnet.new_public_subnet_2d.id
  route_table_id = aws_route_table.new_public_rtb.id
}

### vpc end ###

### ec2 start ###

resource "aws_instance" "example" {
  ami                    = "ami-0fd0765afb77bcca7"
  instance_type          = "t2.micro"
  subnet_id              = aws_subnet.new_public_subnet_2a.id
  vpc_security_group_ids = [aws_security_group.instance.id]
  key_name               = "new-key"
  user_data              = file("user-data.sh")

  tags = {
    Name = "terraform-example"
  }
}

resource "aws_security_group" "instance" {
  vpc_id = aws_vpc.new_vpc.id
  name = var.security_group_name

  ingress {
    from_port   = 80
    to_port     = 80
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }
  ingress {
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = ["123.142.252.25/32"]
  }
  ingress {
    from_port   = -1
    to_port     = -1
    protocol    = "icmp"
    cidr_blocks = ["0.0.0.0/0"]
  }
  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
  tags = {
    Name = "terraform-sg"
  }
}

### ec2 end ###

# vi outputs.tf
output "public_ip" {
  value       = aws_instance.example.public_ip
  description = "The public IP of the Instance"
}

output "public_dns" {
  value       = aws_instance.example.public_dns
  description = "The Public dns of the Instance"
}

output "private_ip" {
  value       = aws_instance.example.private_ip
  description = "The Private_ip of the Instance"
}

# terraform init
# terraform plan
# terraform apply
# terraform output public_ip
# terraform destroy

[AZURE]

 

--- Azure CLI
# mkdir azure_cli && cd $_
# echo -e "[azure-cli]
name=Azure CLI
baseurl=https://packages.microsoft.com/yumrepos/azure-cli
enabled=1
gpgcheck=1
gpgkey=https://packages.microsoft.com/keys/microsoft.asc" | sudo tee /etc/yum.repos.d/azure-cli.repo

저장소를 정의


# yum install -y azure-cli  => 위에서 정의한 저장소에서 최신의 CLI 도구를 내려 받음
# az upgrade
# az --version
# az login

# resourceGroup=VMTutorialResources
# location=koreacentral
# az group create --name $resourceGroup --location $location
# vnetName=TutorialVNet1
# subnetName=TutorialSubnet1
# vnetAddressPrefix=10.133.0.0/16
# subnetAddressPrefix=10.133.0.0/24
# az network vnet create \
--name $vnetName \
--resource-group $resourceGroup \
--address-prefixes $vnetAddressPrefix \
--subnet-name $subnetName \
--subnet-prefixes $subnetAddressPrefix

# az network vnet list
# az vm image list
# vmName=TutorialVM1
# vi httpd.txt
#!/bin/bash
apt update
apt install -y apache2
echo "<h1>Hello Azure CLI</h1>" > /var/www/html/index.html

# az vm create \
--resource-group $resourceGroup \
--name $vmName \
--image UbuntuLTS \
--vnet-name $vnetName \
--subnet $subnetName \
--size Standard_B1s \
--custom-data httpd.txt \
--admin-username azureuser \
--generate-ssh-keys \
--output json \
--verbose

# az network nsg rule create \    => nsg : network security group
--resource-group $resourceGroup \
--nsg-name TutorialVM1NSG \
--name myNetworkSecurityGroupRule \
--protocol tcp \
--priority 900 \
--destination-port-range 80

=> 포트 번호를 오픈하는 방법 1

# az vm open-port -n $vmName -g $resourceGroup --port 443 --priority 999
=> 포트 번호를 오픈하는 방법 2
=> 앞에서 priority 900을 셋팅했으므로 여기서는 999로 설정, priority는 기본이 900
# az vm list-ip-addresses
# ssh -i .ssh/id_rsa azureuser@20.214.201.208
VM에 접속

# az vm delete --resource-group $resourceGroup --name $vmName --yes
=> 특정 vm만 지우고 싶을 때 사용
# az group delete -n $resourceGroup
# az group delete -n NetworkWatcherRG

 

--- azure_set --- 

# git clone https://github.com/hali-linux/azure_set.git
# vi variables.tf
variable "resource_group_name_prefix" {
  default       = "rg"
  description   = "Prefix of the resource group name that's combined with a random ID so name is unique in your Azure subscription."
}

variable "resource_group_location" {
  default = "koreacentral"
  description   = "Location of the resource group."
}

# vi main.tf
provider "azurerm" {
  features {}
}

resource "random_pet" "rg-name" {               => random_pet : 리소스 그룹의 이름을 랜덤으로 만들어주는 기능
  prefix    = var.resource_group_name_prefix
}

resource "azurerm_resource_group" "rg" {  => 리소스 그룹을 만드는 리소스
  name      = random_pet.rg-name.id
  location  = var.resource_group_location
}

# Create virtual network
resource "azurerm_virtual_network" "myterraformnetwork" {
  name                = "myVnet"
  address_space       = ["10.233.0.0/16"]
  location            = azurerm_resource_group.rg.location
  resource_group_name = azurerm_resource_group.rg.name
}

# Create subnet
resource "azurerm_subnet" "myterraformsubnet" {
  name                 = "mySubnet"
  resource_group_name  = azurerm_resource_group.rg.name
  virtual_network_name = azurerm_virtual_network.myterraformnetwork.name
  address_prefixes     = ["10.233.0.0/24"]
}

# Create public IPs
resource "azurerm_public_ip" "myterraformpublicip" {
  name                = "myPublicIP"
  location            = azurerm_resource_group.rg.location
  resource_group_name = azurerm_resource_group.rg.name
  allocation_method   = "Dynamic"
}

# Create Network Security Group and rule
resource "azurerm_network_security_group" "myterraformnsg" {
  name                = "myNetworkSecurityGroup"
  location            = azurerm_resource_group.rg.location
  resource_group_name = azurerm_resource_group.rg.name

  security_rule {
    name                       = "SSH"
    priority                   = 1001
    direction                  = "Inbound"
    access                     = "Allow"
    protocol                   = "Tcp"
    source_port_range          = "*"
    destination_port_range     = "22"
    source_address_prefix      = "*"
    destination_address_prefix = "*"
  }
  security_rule {
    name                       = "HTTP"
    priority                   = 1002
    direction                  = "Inbound"
    access                     = "Allow"
    protocol                   = "Tcp"
    source_port_range          = "*"
    destination_port_range     = "80"
    source_address_prefix      = "*"
    destination_address_prefix = "*"
  }
}   
=> aws에서는 보안 그룹에서 허용 밖에 못하고 거부는 NACL을 통해야 하지만, azure 방화벽은 허용 거부 둘 다 가능
=> azure에서는 egress 셋팅 따로 안 해도 default로 0.0.0.0/0, aws는 egress 셋팅 해줘야 함

# Create network interface
resource "azurerm_network_interface" "myterraformnic" {
  name                = "myNIC"
  location            = azurerm_resource_group.rg.location
  resource_group_name = azurerm_resource_group.rg.name

  ip_configuration {
    name                          = "myNicConfiguration"
    subnet_id                     = azurerm_subnet.myterraformsubnet.id
    private_ip_address_allocation = "Dynamic"
    public_ip_address_id          = azurerm_public_ip.myterraformpublicip.id
  }
}

# Connect the security group to the network interface
resource "azurerm_network_interface_security_group_association" "example" {
  network_interface_id      = azurerm_network_interface.myterraformnic.id
  network_security_group_id = azurerm_network_security_group.myterraformnsg.id
}

# Create (and display) an SSH key
resource "tls_private_key" "example_ssh" {
  algorithm = "RSA"
  rsa_bits  = 4096
}

# Create virtual machine
resource "azurerm_linux_virtual_machine" "myterraformvm" {
  name                  = "myVM"
  location              = azurerm_resource_group.rg.location
  resource_group_name   = azurerm_resource_group.rg.name
  network_interface_ids = [azurerm_network_interface.myterraformnic.id]
  size                  = "Standard_B1s"

  os_disk {                => 블록 스토리지
    name                 = "myOsDisk"
    caching              = "ReadWrite"
    storage_account_type = "Premium_LRS"
  }

  source_image_reference {
    publisher = "Canonical"
    offer     = "UbuntuServer"
    sku       = "18.04-LTS"
    version   = "latest"
  }

  computer_name                   = "myvm"
  admin_username                  = "azureuser"
  custom_data                     = filebase64("httpd-azure.txt")
  disable_password_authentication = true

  admin_ssh_key {
    username   = "azureuser"
    public_key = tls_private_key.example_ssh.public_key_openssh
  }

}