Create and configure AWS instances
A role can create EC2 instances and configure them in one run. You write what the machines are for; the manager chooses the image, network, SSH key and security group, shows its choices in the plan, and creates them on approval.
# AWS instance
- name: web
- count: 2
# Firewall
- open 443/tcp from anywhere
# Packages
- nginx
# Services
- nginx started, enabled
korta roles create --file web.md --name web
korta roles run role_ID
In the console: Roles > New role, insert AWS instance, save, then Run. The instances appear under Instances and can then be used by other roles and by database or application deployments.
Set up the manager once
Add AWS accounts under Settings > AWS > Accounts. No restart is needed, and roles never contain credentials. Each account signs in one of three ways:
| Sign in by | Use when | Korta stores |
|---|---|---|
| Assume a role (recommended) | Production, and several accounts | The role ARN and an optional external ID. Korta gets short-lived credentials for each run |
| Access key | A quick start, or a manager outside AWS | The access key ID, and the secret access key sealed in the vault, never shown again |
| Manager's environment | The manager has its own IAM role or AWS variables | Nothing; environment variable changes need a restart |
For Assume a role, create an IAM role in the target account with the policy below, and let the manager's identity assume it. That identity is the manager's own IAM role, or an access key you enter on the same account entry:
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": { "AWS": "arn:aws:iam::MANAGER_ACCOUNT_ID:role/korta-manager" },
"Action": "sts:AssumeRole",
"Condition": { "StringEquals": { "sts:ExternalId": "YOUR_EXTERNAL_ID" } }
}]
}
Give each account a default region. Mark one account default: roles
without a network use it. Networks name their account, so a role reaches the
prod account by saying network: prod. With no accounts at all, the manager's
environment applies (AWS_REGION and its IAM role or variables), as in earlier
releases.
Test on an account signs in and shows which AWS account it reaches. Plans record the account entry they used: instances stay with it, and a role whose network or default would move them to another account is refused until that is undone.
Each account's credentials need this IAM policy. Terminating and deleting are limited to resources Korta tagged:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "KortaRead",
"Effect": "Allow",
"Action": [
"sts:GetCallerIdentity",
"ec2:DescribeInstances", "ec2:DescribeImages", "ec2:DescribeInstanceTypes",
"ec2:DescribeVpcs", "ec2:DescribeSubnets", "ec2:DescribeKeyPairs",
"ec2:DescribeSecurityGroups", "ec2:DescribeSecurityGroupRules"
],
"Resource": "*"
},
{
"Sid": "KortaCreate",
"Effect": "Allow",
"Action": [
"ec2:RunInstances", "ec2:CreateTags", "ec2:ImportKeyPair",
"ec2:CreateSecurityGroup", "ec2:AuthorizeSecurityGroupIngress",
"ec2:RevokeSecurityGroupIngress"
],
"Resource": "*"
},
{
"Sid": "KortaDestroyInstances",
"Effect": "Allow",
"Action": "ec2:TerminateInstances",
"Resource": "*",
"Condition": { "Null": { "aws:ResourceTag/korta:launch": "false" } }
},
{
"Sid": "KortaDestroyGroupsAndKeys",
"Effect": "Allow",
"Action": ["ec2:DeleteSecurityGroup", "ec2:DeleteKeyPair"],
"Resource": "*",
"Condition": { "Null": { "aws:ResourceTag/korta:role": "false" } }
}
]
}
Narrow RunInstances further if your installation should only use certain
instance types, subnets or AMI owners. The manager also reaches
https://checkip.amazonaws.com to learn its public address for SSH rules.
What Korta chooses
| Field | Default | Notes |
|---|---|---|
name |
required | With count, instances are web, web-2, web-3 |
count |
1 |
Up to 20. Raise it later to add instances; it never shrinks |
os |
ubuntu 24.04 |
Ubuntu 22.04/24.04, Debian 12/13, RHEL, Rocky or AlmaLinux 8 to 10, CentOS Stream 9/10 |
size |
t3.micro |
Any instance type; Arm types such as t4g.small get Arm images |
network |
the region's default VPC | A network defined under Settings > AWS |
region |
the manager's | Must match the network's region when both are set |
address |
public |
Or the network's address |
static ip |
no |
yes gives each instance an Elastic IP that survives stop and start, for DNS and allow-lists. Switching it off later releases the IPs and the instances get new public addresses |
- Image: the newest official image of
osfrom its publisher (Canonical, Debian, Red Hat, Rocky, AlmaLinux, CentOS), matching the instance type's CPU, and that image's login user. The plan names the exact AMI, and approval launches exactly that one; later runs plan a newer image only for new instances. - Network: instances are spread over the network's subnets, or the default VPC's default subnets, one per zone in turn.
- SSH key: Korta creates an Ed25519 key in its vault and imports the
public half as EC2 key pair
korta-<role>-<id>. - Security group:
korta-<role>-<id>, allowing SSH from the manager (its public address; the VPC for private addresses; or the network's SSH from) and each port the role's# Firewallsection opens. Korta keeps the group's rules equal to that on every run, so removing a firewall line closes the port in AWS too.
To use your own resources instead, set them in the role: image with user,
subnet, security-groups (Korta then creates no group), or key-pair with
the vault ssh-key holding its private key (import it under SSH keys).
Load balancers
Add an # AWS load balancer section right after # AWS instance to put an
Application Load Balancer in front of the role's instances:
# AWS instance
- name: web
- count: 3
- network: prod
- address: private
# AWS load balancer
- certificate: arn:aws:acm:eu-west-1:123456789012:certificate/…
- forward: 8080
- health check: /health
| Field | Default | Meaning |
|---|---|---|
listen |
http, or https with a certificate |
https serves port 443 with the certificate and redirects port 80 to it |
certificate |
none | The ARN of an ACM certificate in the instances' region |
forward |
80 |
The instances' port |
health check |
/ |
A path that answers 200-399 on healthy instances |
from |
0.0.0.0/0 |
Networks that may reach the load balancer |
internal |
no |
yes keeps it inside the VPC |
Korta creates the load balancer in two or more zones (an internet-facing one
in the network's public subnets, so instances can stay private), its target
group and listeners, and a security group that lets only the load balancer
reach the instances' forward port. It does this after the instances are
configured, registers new instances as count grows, and keeps listeners and
the health check in step with the section. Removing the section deletes the
load balancer; Destroy does too. The role's page shows its address, which DNS
names can point to. With your own security-groups, allow the forward port
from the load balancer's group yourself. Changing internal later needs the
section removed and added back.
Load balancers need elasticloadbalancing:CreateLoadBalancer,
DescribeLoadBalancers, DeleteLoadBalancer, CreateTargetGroup,
DescribeTargetGroups, ModifyTargetGroup, DeleteTargetGroup,
RegisterTargets, DeregisterTargets, DescribeTargetHealth,
CreateListener, DescribeListeners, ModifyListener, DeleteListener and
AddTags, plus iam:CreateServiceLinkedRole the first time an account uses
Elastic Load Balancing.
DNS names and certificates
An # AWS DNS section, after # AWS instance and its load balancer, lists
names for the role, one per bullet:
# AWS load balancer
- certificate: auto
- forward: 8080
# AWS DNS
- shop.example.com
- www.shop.example.com
Each name goes in the Route 53 hosted zone of the account whose name is its
longest match: a public zone, or a private one when only the VPC can reach
the role (an internal load balancer, or private addresses). With a load
balancer, names are alias records pointing at it; otherwise they list the
instances' addresses, so use static ip: yes for public instances.
certificate: auto asks ACM for a certificate for these names, proves them
with validation records in Route 53, waits until it is issued (usually a few
minutes) and serves it on port 443.
Korta only changes records it created. A name that already has a record
Korta did not make stops the plan; delete it in Route 53 or choose another
name. Removing a name deletes its record; replacing names gets a new
certificate and deletes the old one once the load balancer stops using it.
Destroy deletes the records and the certificate. Wildcards such as
*.shop.example.com work for records and certificates.
DNS needs route53:ListHostedZones, route53:ListResourceRecordSets and
route53:ChangeResourceRecordSets; certificates need
acm:RequestCertificate, acm:DescribeCertificate, acm:DeleteCertificate
and acm:AddTagsToCertificate.
Networks
Instances go to the region's default VPC unless their role names a network. There are two kinds:
Built by a role. A role with only an # AWS network section creates a VPC
with public and private subnets across zones, an internet gateway, NAT gateways
and routes, or adopts an existing VPC:
# AWS network
- name: prod
- region: eu-west-1
- cidr: 10.20.0.0/16
- zones: 3
# AWS network
- name: legacy
- vpc: vpc-0123456789abcdef0
- public subnets: subnet-0aaa…, subnet-0bbb…
- private subnets: 10.1.10.0/24, 10.1.11.0/24
Plan it to see every resource it would create, then approve. In an adopted
VPC, Korta only adds its own tagged subnets, gateways, routes and NAT gateways;
it never changes or deletes what was there, and existing subnets keep their
routes. Once built, a network can gain zones, NAT gateways and SSH sources;
its region, account, VPC and ranges are fixed. Destroy deletes what Korta
created, after the instance roles that use the network are destroyed. NAT
gateways are billed hourly; nat: none leaves private subnets without
internet access, and nat: one shares one gateway across zones.
Defined in Settings. For subnets that already exist, an admin adds a network under Settings > AWS with its account, region, subnets, address, optional SSH from networks (such as your VPN range) and extra security groups attached to every instance (such as a monitoring group).
Instance roles use either kind the same way:
# AWS instance
- name: api
- count: 3
- network: prod
- address: private
address: private places instances in the private subnets and connects over
their private addresses, so the manager must reach the VPC through VPN,
peering or by running inside it. Otherwise instances go to the public subnets.
Networks need these permissions as well: ec2:DescribeAvailabilityZones,
ec2:DescribeTags, ec2:CreateVpc, ec2:ModifyVpcAttribute, ec2:DeleteVpc,
ec2:DescribeInternetGateways, ec2:CreateInternetGateway,
ec2:AttachInternetGateway, ec2:DetachInternetGateway,
ec2:DeleteInternetGateway, ec2:CreateSubnet, ec2:ModifySubnetAttribute,
ec2:DeleteSubnet, ec2:CreateRouteTable, ec2:DescribeRouteTables,
ec2:CreateRoute, ec2:AssociateRouteTable, ec2:DisassociateRouteTable,
ec2:DeleteRouteTable, ec2:AllocateAddress, ec2:ReleaseAddress,
ec2:CreateNatGateway, ec2:DescribeNatGateways and ec2:DeleteNatGateway.
Static IPs also need ec2:DescribeAddresses, ec2:AssociateAddress and
ec2:DisassociateAddress.
Run, approve, destroy
Run plans, then approves on the same screen. The plan creates nothing: it shows the image, subnets, security group rules and the configuration that will run, and checks launch permission with a dry run. Approval creates the key pair and security group, launches the instances, waits for SSH and applies the rest of the role. With four-eyes approval on (the default), the person who changed or planned the role cannot approve it; the plan waits on the role's page for someone else.
Running the role again configures the instances, keeps the security group in
step with the Firewall section, and launches any instances a larger count
adds. Launch settings (os, size, network, region, address and your own
resources) are fixed once instances exist.
Destroy on the role's page (or korta roles destroy role_ID --confirm NAME)
plans removing what the role created and, once approved with the role's name
typed in, terminates the instances, then deletes the security group, key pair
and vault key. A role with AWS instances cannot be deleted until they are
destroyed, and instances used by a deployment cannot be destroyed until the
deployment is deleted.
Creating, approving and destroying AWS roles need a global admin or owner grant.
Retries
Korta saves a launch reservation before calling AWS. If a launch response is lost or the manager restarts, run the role again: Korta recovers the instance with the same client token instead of launching a second one. Failed configuration keeps the instances for inspection; running the role again retries. Keep the manager's state backed up: it holds the reservations.
korta