diff --git a/.gitignore b/.gitignore index 30d74d2584..e8384ee8c7 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,18 @@ -test \ No newline at end of file +test +# Terraform +terraform/.terraform/ +terraform/.terraform.lock.hcl +terraform/terraform.tfstate +terraform/terraform.tfstate.backup +terraform/*.tfvars + +# Yandex Cloud SA key +.yc/ + +venv/ +*/venv/ +__pycache__/ +.terraform/ +.pulumi/ +*.tfstate +*.tfstate.backup diff --git a/pulumi/lab04-yc/.gitignore b/pulumi/lab04-yc/.gitignore new file mode 100644 index 0000000000..a3807e5bdb --- /dev/null +++ b/pulumi/lab04-yc/.gitignore @@ -0,0 +1,2 @@ +*.pyc +venv/ diff --git a/pulumi/lab04-yc/Pulumi.yaml b/pulumi/lab04-yc/Pulumi.yaml new file mode 100644 index 0000000000..82f52af683 --- /dev/null +++ b/pulumi/lab04-yc/Pulumi.yaml @@ -0,0 +1,7 @@ +name: lab04-yc +description: A minimal Python Pulumi program +runtime: python +config: + pulumi:tags: + value: + pulumi:template: python diff --git a/pulumi/lab04-yc/__main__.py b/pulumi/lab04-yc/__main__.py new file mode 100644 index 0000000000..5703481991 --- /dev/null +++ b/pulumi/lab04-yc/__main__.py @@ -0,0 +1,128 @@ +import os +import pathlib + +import pulumi +import pulumi_yandex as yandex + + + +ZONE = "ru-central1-a" + + +FOLDER_ID = "b1g1cmmbss046n25oln3" + +SSH_PUBLIC_KEY_PATH = os.path.expanduser("~/.ssh/id_ed25519.pub") + +SSH_USERNAME = "ubuntu" + + + +def read_ssh_public_key(path: str) -> str: + p = pathlib.Path(path) + if not p.exists(): + raise FileNotFoundError(f"SSH public key not found: {p}") + return p.read_text().strip() + + +ssh_pub = read_ssh_public_key(SSH_PUBLIC_KEY_PATH) + + + +net = yandex.VpcNetwork( + "lab-network", + folder_id=FOLDER_ID, +) + +subnet = yandex.VpcSubnet( + "lab-subnet", + folder_id=FOLDER_ID, + network_id=net.id, + zone=ZONE, + v4_cidr_blocks=["10.0.0.0/24"], +) + + + +sg = yandex.VpcSecurityGroup( + "lab-sg", + folder_id=FOLDER_ID, + network_id=net.id, + description="Security group for lab04 VM (SSH, HTTP, app port)", + egresses=[ + yandex.VpcSecurityGroupEgressArgs( + protocol="ANY", + description="Allow all outbound", + v4_cidr_blocks=["0.0.0.0/0"], + from_port=0, + to_port=65535, + ) + ], + ingresses=[ + yandex.VpcSecurityGroupIngressArgs( + protocol="TCP", + description="SSH", + v4_cidr_blocks=["0.0.0.0/0"], + port=22, + ), + yandex.VpcSecurityGroupIngressArgs( + protocol="TCP", + description="HTTP", + v4_cidr_blocks=["0.0.0.0/0"], + port=80, + ), + yandex.VpcSecurityGroupIngressArgs( + protocol="TCP", + description="App port 5000", + v4_cidr_blocks=["0.0.0.0/0"], + port=5000, + ), + ], +) + + + +image = yandex.get_compute_image( + family="ubuntu-2004-lts", + folder_id="standard-images", +) + + + +vm = yandex.ComputeInstance( + "lab-vm", + folder_id=FOLDER_ID, + zone=ZONE, + platform_id="standard-v2", + resources=yandex.ComputeInstanceResourcesArgs( + cores=2, + memory=1, + core_fraction=20, + ), + boot_disk=yandex.ComputeInstanceBootDiskArgs( + initialize_params=yandex.ComputeInstanceBootDiskInitializeParamsArgs( + image_id=image.id, + size=10, + type="network-hdd", + ) + ), + network_interfaces=[ + yandex.ComputeInstanceNetworkInterfaceArgs( + subnet_id=subnet.id, + nat=True, + security_group_ids=[sg.id], + ) + ], + metadata={ + "ssh-keys": f"{SSH_USERNAME}:{ssh_pub}", + }, + labels={ + "lab": "lab04", + "tool": "pulumi", + }, +) + + +pulumi.export("external_ip", vm.network_interfaces[0].nat_ip_address) +pulumi.export("zone", vm.zone) +pulumi.export("subnet_id", subnet.id) +pulumi.export("security_group_id", sg.id) diff --git a/pulumi/lab04-yc/requirements.txt b/pulumi/lab04-yc/requirements.txt new file mode 100644 index 0000000000..bc4e43087b --- /dev/null +++ b/pulumi/lab04-yc/requirements.txt @@ -0,0 +1 @@ +pulumi>=3.0.0,<4.0.0 diff --git a/terraform/docs/LAB04.md b/terraform/docs/LAB04.md new file mode 100644 index 0000000000..72ff4cee9d --- /dev/null +++ b/terraform/docs/LAB04.md @@ -0,0 +1,212 @@ +# LAB04 — Infrastructure as Code (Terraform & Pulumi) + +## 1. Cloud Provider & Infrastructure + +**Cloud Provider:** Yandex Cloud +**Folder ID:** b1g1cmmbss046n25oln3 +**Region / Zone:** ru-central1-a +**Instance Type:** standard-v2 (2 vCPU with 20% core fraction, 1 GB RAM) +**Disk:** 10 GB HDD +**Operating System:** Ubuntu 24.04 LTS + +The smallest available instance type compatible with Yandex Cloud free tier was selected to minimize cost. + +### Security Configuration + +The following ports are opened in the security group: + +- TCP 22 — SSH (restricted access for remote management) +- TCP 80 — HTTP (future deployment) +- TCP 5000 — Application port (DevOps Info Service from previous labs) + +### Created Resources + +- VPC Network (`lab-network`) +- Subnet (`lab-subnet`) +- Security Group (`lab-sg`) +- Virtual Machine (`lab-vm`) +- Public IP Address + +Estimated cost: **0 RUB** (free tier usage). +Terraform Version: 1.9.8 +Pulumi Version: 3.222.0 + +--- + +## 2. Terraform Implementation + +### Terraform Version +Terraform CLI 1.9.x (Ubuntu Linux) + +### Project Structure + +terraform/ +├── main.tf +├── variables.tf +├── outputs.tf +└── docs/LAB04.md + + +### Authentication + +Authentication was configured using a Yandex Cloud service account JSON key: +~/.yc/terraform-key.json + + +Provider configuration: + +```hcl +provider "yandex" { + service_account_key_file = pathexpand("~/.yc/terraform-key.json") + folder_id = var.folder_id + zone = var.zone +} +``` + +### Workflow + +``` +terraform init +terraform fmt +terraform validate +terraform plan +terraform apply +``` + + +Example output from terraform plan: +Plan: 3 to add, 0 to change, 0 to destroy. + +Example output from terraform apply: +Apply complete! Resources: 3 added, 0 changed, 0 destroyed. + +Outputs: + +external_ip = "X.X.X.X" + + +SSH Verification +ssh ubuntu@ + + +SSH connection was successful. + +Cleanup + +After verifying functionality, Terraform resources were destroyed: + +terraform destroy + + +All resources created by Terraform were removed successfully to avoid duplication and unnecessary usage. + +## 3. Pulumi Implementation +Pulumi Version + +Pulumi CLI v3.222.0 + +Language + +Python + +Project Structure +pulumi/lab04-yc/ + ├── Pulumi.yaml + ├── Pulumi.dev.yaml + ├── requirements.txt + ├── __main__.py + └── venv/ + +Authentication + +Pulumi uses the same Yandex Cloud service account key: + +export YC_SERVICE_ACCOUNT_KEY_FILE=/home/vboxuser/.yc/terraform-key.json + +Resources Created + +The same infrastructure was recreated using Pulumi: + +VpcNetwork + +VpcSubnet + +VpcSecurityGroup + +ComputeInstance + +Public IP + +Pulumi Commands +pulumi preview +pulumi up + + +Preview example: + ++ yandex:index:VpcNetwork ++ yandex:index:VpcSubnet ++ yandex:index:VpcSecurityGroup ++ yandex:index:ComputeInstance + + +Apply output: + +Outputs: + external_ip : "93.77.190.119" + zone : "ru-central1-a" + +SSH Verification +ssh ubuntu@93.77.190.119 + + +SSH access was successful. + +## 4. Terraform vs Pulumi Comparison +Ease of Learning + +Terraform was easier to start with due to extensive documentation and straightforward declarative syntax. Pulumi required more setup (virtual environments, Python dependencies). + +Code Readability + +Terraform configurations are compact and declarative, making them easy to read for simple infrastructure. Pulumi provides more flexibility but adds programming complexity. + +Debugging + +Terraform errors are generally clear during plan and apply. Pulumi provides Python stack traces, which can be more detailed but sometimes harder to interpret. + +Documentation + +Terraform has broader documentation and community examples. Pulumi documentation is solid but less extensive for Yandex Cloud specifically. + +Use Case Preference + +Terraform is preferable for straightforward infrastructure definitions. +Pulumi is more powerful when complex logic, loops, or programming constructs are required. + +## 5. Lab 5 Preparation & Cleanup + +For Lab 5 (Ansible), the VM created using Pulumi will be kept active. + +Active VM: + +IP Address: 93.77.190.119 +Zone: ru-central1-a +User: ubuntu + +Terraform resources were destroyed. +Pulumi-managed VM remains running for future configuration management tasks. + +No secrets or state files were committed to Git. + +Infrastructure can be recreated at any time using: + +terraform apply + + +or + +pulumi up + +Terraform state was stored locally. The file terraform.tfstate was added to .gitignore and not committed to the repository. + diff --git a/terraform/docs/screenshots/pulumi-up.png b/terraform/docs/screenshots/pulumi-up.png new file mode 100644 index 0000000000..a6e37bd63d Binary files /dev/null and b/terraform/docs/screenshots/pulumi-up.png differ diff --git a/terraform/docs/screenshots/terraform-apply.png b/terraform/docs/screenshots/terraform-apply.png new file mode 100644 index 0000000000..e9c3bc3afd Binary files /dev/null and b/terraform/docs/screenshots/terraform-apply.png differ diff --git a/terraform/docs/screenshots/yc-vm-running.png b/terraform/docs/screenshots/yc-vm-running.png new file mode 100644 index 0000000000..7a87ed9991 Binary files /dev/null and b/terraform/docs/screenshots/yc-vm-running.png differ diff --git a/terraform/main.tf b/terraform/main.tf new file mode 100644 index 0000000000..62ed0354df --- /dev/null +++ b/terraform/main.tf @@ -0,0 +1,56 @@ +terraform { + required_providers { + yandex = { + source = "yandex-cloud/yandex" + } + } +} + +provider "yandex" { + service_account_key_file = "/home/vboxuser/.yc/terraform-key.json" + cloud_id = "b1g0qsmtu1cheeq79i0d" + folder_id = "b1g1cmmbss046n25oln3" + zone = "ru-central1-a" +} +resource "yandex_vpc_network" "lab_network" { + name = "lab-network" +} + +resource "yandex_vpc_subnet" "lab_subnet" { + name = "lab-subnet" + zone = "ru-central1-a" + network_id = yandex_vpc_network.lab_network.id + v4_cidr_blocks = ["10.10.0.0/24"] +} + +resource "yandex_compute_instance" "lab_vm" { + name = "lab-vm" + zone = "ru-central1-a" + platform_id = "standard-v2" + + resources { + cores = 2 + memory = 1 + core_fraction = 20 + } + + boot_disk { + initialize_params { + image_id = "fd80bm0rh4rkepi5ksdi" # Ubuntu 24.04 LTS + size = 10 + } + } + + network_interface { + subnet_id = yandex_vpc_subnet.lab_subnet.id + nat = true + } + + metadata = { + ssh-keys = "ubuntu:${file("/home/vboxuser/.ssh/id_ed25519.pub")}" + } +} + +output "external_ip" { + value = yandex_compute_instance.lab_vm.network_interface.0.nat_ip_address +}