> ## Documentation Index
> Fetch the complete documentation index at: https://gcore.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Add and configure a firewall

export const MethodSection = ({children}) => children ?? null;

export const MethodSwitch = ({children}) => {
  const tabs = React.Children.toArray(children).map(c => {
    if (!c || !c.props) return null;
    if (c.props.id) return c;
    const inner = c.props.children;
    if (inner && inner.props && inner.props.id) return inner;
    return null;
  }).filter(Boolean);
  const firstId = tabs.length > 0 ? tabs[0].props.id : "";
  const [active, setActive] = React.useState(firstId);
  React.useEffect(() => {
    try {
      const saved = localStorage.getItem("gcore_docs_method");
      if (saved && tabs.find(t => t.props.id === saved)) {
        setActive(saved);
      }
    } catch (_) {}
  }, []);
  React.useEffect(() => {
    try {
      document.querySelectorAll("h2[id], h3[id]").forEach(heading => {
        const visible = heading.offsetParent !== null;
        document.querySelectorAll(`a[href="#${heading.id}"]`).forEach(link => {
          if (link.closest("h1,h2,h3,h4,h5,h6")) return;
          const li = link.closest("li");
          if (li) li.style.display = visible ? "" : "none";
        });
      });
    } catch (_) {}
    window.dispatchEvent(new Event("scroll"));
  }, [active]);
  const handleClick = id => {
    setActive(id);
    try {
      localStorage.setItem("gcore_docs_method", id);
    } catch (_) {}
  };
  return <div>
      <div className="not-prose flex gap-0 border-b border-zinc-200 dark:border-zinc-800 mb-8 mt-2" role="tablist">
        {tabs.map(tab => {
    const isActive = active === tab.props.id;
    return <button key={tab.props.id} role="tab" aria-selected={isActive} onClick={() => handleClick(tab.props.id)} className={["px-4 py-2 text-sm font-medium border-b-2 -mb-px transition-colors cursor-pointer", isActive ? "border-primary text-primary" : "border-transparent text-zinc-500 hover:text-zinc-800 dark:hover:text-zinc-200"].join(" ")}>
              {tab.props.label}
            </button>;
  })}
      </div>

      {tabs.map(tab => <div key={tab.props.id} style={{
    display: active === tab.props.id ? "" : "none"
  }}>
          {tab.props.children}
        </div>)}
    </div>;
};

<MethodSwitch>
  <MethodSection id="portal" label="Customer Portal">
    <p>A firewall is a network security device used to protect servers from network threats. The firewall monitors incoming and outgoing network traffic and decides whether to allow or block specific traffic based on a defined set of security rules. Rules can be set for all connections.</p>

    <p>When a Virtual Machine is in a [Load Balancer](/cloud/networking/create-and-configure-a-load-balancer) pool, configure its security group to open the ports required for receiving and transmitting data.</p>

    ## Create a firewall

    <p>If no custom security group is created, the default security group is applied automatically.</p>

    1. Open the creation form in one of two ways:

       * In the Cloud menu, go to **Networking** > **Security Groups** > **Create Security Group**.
       * When creating a Virtual Machine, go to **Network settings**, expand an interface, and configure **Security Groups** for that interface.

    <Frame>
      <img src="https://mintcdn.com/gcore/ozZXFrd5YDD-9KSI/images/docs/cloud/networking/add-and-configure-a-firewall/security-groups-list.png?fit=max&auto=format&n=ozZXFrd5YDD-9KSI&q=85&s=38155659f2d86cc14bd357094c044939" alt="Security Groups list with Create Security Group button" width="1317" height="836" data-path="images/docs/cloud/networking/add-and-configure-a-firewall/security-groups-list.png" />
    </Frame>

    2. Enter a name for the security group.

    3. Set **Inbound rules** to define the allowed incoming traffic.
       <p>Click **Add rule** and select one of the template rules, or choose **Custom** to apply custom settings. A security group supports up to 250 rules total across inbound and outbound directions.</p>

       * Template rules (All TCP/All UDP/SSH/HTTP/HTTPS/MySQL/DNS TCP/DNS UDP/PostgreSQL/RDP TCP/RDP UDP): template rules come with pre-configured protocols and ports for typical connections
       * Custom rule: if you select a custom rule, specify the protocol, port, and optionally a description.

    <Frame>
      <img src="https://mintcdn.com/gcore/yRbjcWNVIGy6hUs4/images/docs/cloud/networking/add-and-configure-a-firewall/firewall-add-rule-templates.png?fit=max&auto=format&n=yRbjcWNVIGy6hUs4&q=85&s=097b91840ca6072ad4bc1a4288c4675e" alt="Add rule dropdown showing template options including RDP TCP and RDP UDP" width="1292" height="900" data-path="images/docs/cloud/networking/add-and-configure-a-firewall/firewall-add-rule-templates.png" />
    </Frame>

    <p>For **Sources**, specify the allowed sources for inbound traffic:</p>

    * **IP with mask**: Specify an IP address range in CIDR format (for example, 192.168.1.0/24). If left empty with "All IPv4" selected, the rule applies to all IP addresses.
    * **Select security groups**: Choose existing security groups to allow traffic from the instances associated with those groups.

    <p>Both options can be combined. If neither is specified, the rule applies to all IP addresses.</p>

    <p>Multiple security groups can be attached to a single Virtual Machine, and each port (interface) can have its own dedicated security group.</p>

    4. Set **Outbound rules** to define the allowed outgoing traffic.

    <Info>
      By default, all outbound traffic is allowed. Add outbound rules only if you need to restrict specific destinations or protocols.
    </Info>

    <p>Click **Add rule** and select one of the template rules, or choose **Custom** to apply custom settings.</p>

    * Template rules (All TCP/All UDP/SSH/HTTP/HTTPS/MySQL/DNS TCP/DNS UDP/PostgreSQL/RDP TCP/RDP UDP): template rules come with pre-configured protocols and ports for typical connections
    * Custom rule: if you select a custom rule, specify the protocol, port, and optionally a description.

    <Frame>
      <img src="https://mintcdn.com/gcore/yRbjcWNVIGy6hUs4/images/docs/cloud/networking/add-and-configure-a-firewall/firewall-add-rule-sources.png?fit=max&auto=format&n=yRbjcWNVIGy6hUs4&q=85&s=7563a701b7d7aeb688ddb5d352ca8999" alt="Add firewall rule dialog showing protocol, port, and sources configuration" width="1292" height="900" data-path="images/docs/cloud/networking/add-and-configure-a-firewall/firewall-add-rule-sources.png" />
    </Frame>

    <p>For **Destinations**, specify the allowed destinations for outbound traffic:</p>

    * **IP with mask**: Specify an IP address range in CIDR format (for example, 192.168.1.0/24). If left empty with "All IPv4" selected, the rule applies to all IP addresses.
    * **Select security groups**: Choose existing security groups to allow traffic to the instances associated with those groups.

    <p>Both options can be combined. If neither is specified, the rule applies to all IP addresses.</p>

    <p>Rules take effect immediately and apply to all Virtual Machines currently using this security group.</p>

    5. (Optional) Add tags by enabling **Add tags** in the **Additional options** section and specifying a key and value for each tag.

    6. Click **Create Security Group**.

    ## Use the default firewall

    <p>If no security group is specified when creating a Virtual Machine, the default security group is applied automatically. The system creates a default security group in the default project when the service is activated.</p>

    <p><strong>Inbound rules</strong> — the following traffic is allowed for both IPv4 and IPv6 addresses:</p>

    * ICMP (IPv4) and ICMPv6 (IPv6)
    * SSH over TCP on port 22
    * RDP over TCP on port 3389
    * RDP over UDP on port 3389

    <p><strong>Outbound rules</strong> — all traffic is allowed.</p>

    <Info>
      Instances in the same security group no longer communicate with each other automatically. The self-referential rule that previously allowed intra-group traffic has been removed. If your setup relies on it, add an inbound rule that allows traffic from the security group itself.
    </Info>

    <p>Some operating systems also have an internal firewall that adds an additional layer of protection. To prevent misconfiguration, configure either the cloud security group or the OS firewall, but not both.</p>

    ## Manage a firewall

    <p>Security groups protect cloud infrastructure by filtering network traffic based on defined rules. The steps below cover configuring rules, assigning security groups to Virtual Machines, and deleting them.</p>

    ### Add, change, and delete rules

    <p>Rules can be added, edited, or removed at any time after a security group is created.</p>

    1. Go to **Networking** > **Security Groups**.

    2. Find the required security group, click the Γï» menu on the right and select **Rules**.

    <Frame>
      <img src="https://mintcdn.com/gcore/ozZXFrd5YDD-9KSI/images/docs/cloud/networking/add-and-configure-a-firewall/security-group-context-menu.png?fit=max&auto=format&n=ozZXFrd5YDD-9KSI&q=85&s=3f0c3e1a72d5fd39677eb907eb5480f9" alt="Security group context menu showing Duplicate, Rules, and Instances options" width="1317" height="836" data-path="images/docs/cloud/networking/add-and-configure-a-firewall/security-group-context-menu.png" />
    </Frame>

    ### Assign to a Virtual Machine and detach from it

    <p>Security groups can be duplicated, assigned to Virtual Machines, or detached when no longer needed. To copy an existing security group with all its rules, click the Γï» menu and select **Duplicate**. If the target security group is already applied to the Virtual Machine, no changes are made: the system automatically detects the current state and skips unnecessary updates.</p>

    1. Go to **Networking** > **Security Groups**.

    2. Find the required security group, click the Γï» menu on the right and select **Instances**.

    <Frame>
      <img src="https://mintcdn.com/gcore/yRbjcWNVIGy6hUs4/images/docs/cloud/networking/add-and-configure-a-firewall/security-group-instances.png?fit=max&auto=format&n=yRbjcWNVIGy6hUs4&q=85&s=881897690b02de32528c9c26f01e95e1" alt="Security group instances list showing assigned Virtual Machines" width="801" height="293" data-path="images/docs/cloud/networking/add-and-configure-a-firewall/security-group-instances.png" />
    </Frame>

    ### Delete a security group

    <p>The default security group cannot be deleted.</p>

    1. Go to **Networking** > **Security Groups**.

    2. Find the required security group, click the Γï» menu on the right and select **Delete**.

    ### Restore a default security group

    <p>To restore the original rules, click **Restore Default** in the security group Rules view or use the API. This resets any custom changes to the current default rule set: inbound ICMP, ICMPv6, SSH (TCP/22), and RDP (TCP/3389 and UDP/3389); outbound unrestricted.</p>

    <Frame>
      <img src="https://mintcdn.com/gcore/ozZXFrd5YDD-9KSI/images/docs/cloud/networking/add-and-configure-a-firewall/restore-default-option.png?fit=max&auto=format&n=ozZXFrd5YDD-9KSI&q=85&s=d5eb2aa26805c4b1726ab54f56e0c7ce" alt="Restore Default option for resetting firewall settings" width="1317" height="836" data-path="images/docs/cloud/networking/add-and-configure-a-firewall/restore-default-option.png" />
    </Frame>

    ## Firewall feature not supported for Bare Metal servers

    <Info>
      The Firewall feature is not supported for Bare Metal servers. Unlike Virtual Machines or other cloud services that integrate with cloud-native firewalls, Bare Metal servers operate directly on physical hardware and are not subject to the same level of firewall management.
    </Info>

    <p>For network security, Bare Metal servers can use the following alternatives:</p>

    * Configure network security manually using `iptables`, `nftables`, or similar tools at the OS level. For example, to allow SSH, HTTP, and HTTPS traffic while blocking everything else:

    ```sh theme={null}
    sudo iptables -A INPUT -p tcp --dport 22 -j ACCEPT
    sudo iptables -A INPUT -p tcp --dport 80 -j ACCEPT
    sudo iptables -A INPUT -p tcp --dport 443 -j ACCEPT
    sudo iptables -A INPUT -i lo -j ACCEPT
    sudo iptables -P INPUT DROP
    ```

    * Enable [Gcore DDoS Protection](https://gcore.com/ddos-protection) for an additional protection layer. DDoS Protection redirects traffic to the Threat Mitigation System (TMS), which performs filtering and threat detection with its always-on mode. Contact the [support team](https://gcore.com/contact-us) for tailored plans.
  </MethodSection>

  <MethodSection id="api" label="REST API">
    <p>A [security group](/api-reference/cloud/security-groups/create-security-group) controls which traffic is allowed in and out of a virtual machine.</p>

    <Info>
      An [API token](/account-settings/api-tokens) is required, along with a [project ID](/api-reference/cloud/projects/list-projects) and [region ID](/api-reference/cloud/regions/list-regions).
    </Info>

    <p>Set these environment variables before running the examples:</p>

    ```bash theme={null}
    export GCORE_API_KEY="{YOUR_API_KEY}"
    export GCORE_CLOUD_PROJECT_ID="{YOUR_PROJECT_ID}"
    export GCORE_CLOUD_REGION_ID="{YOUR_REGION_ID}"
    export INSTANCE_ID="{YOUR_INSTANCE_ID}"
    export IMAGE_ID="{YOUR_IMAGE_ID}"
    export FLAVOR_ID="{YOUR_FLAVOR_ID}"
    export SG_ID="{YOUR_SECURITY_GROUP_ID}"
    ```

    <Info>
      To find the values for each variable:

      * **INSTANCE\_ID**: the UUID of a virtual machine in your region. To create one, see [Create a virtual machine](/cloud/virtual-instances/create-an-instance).
      * **IMAGE\_ID**: the UUID of a public or private image. Use `GET /cloud/v1/images/{project_id}/{region_id}` to list available images.
      * **FLAVOR\_ID**: the flavor identifier for the VM size. Use `GET /cloud/v1/flavors/{project_id}/{region_id}` to list flavors available in your region.
      * **SG\_ID**: the UUID of an existing security group. Created in the [Create a security group](#create-a-security-group) section below, or use `GET /cloud/v2/security_groups/{project_id}/{region_id}` to list existing ones.
    </Info>

    ## Quickstart

    <p>The scripts below create a security group with SSH, HTTP, and HTTPS rules, attach it to a running VM, add an ICMP rule, then detach and delete the group.</p>

    <Tabs>
      <Tab title="Python SDK">
        ```python theme={null}
        import os
        from gcore import Gcore
        from gcore.types.cloud import security_group_create_params

        client = Gcore()

        # Step 1. Create a security group with inbound rules.
        sg = client.cloud.security_groups.create_and_poll(
            name="web-firewall",
            rules=[
                security_group_create_params.Rule(direction="ingress", protocol="tcp", port_range_min=22, port_range_max=22, remote_ip_prefix="0.0.0.0/0"),
                security_group_create_params.Rule(direction="ingress", protocol="tcp", port_range_min=80, port_range_max=80, remote_ip_prefix="0.0.0.0/0"),
                security_group_create_params.Rule(direction="ingress", protocol="tcp", port_range_min=443, port_range_max=443, remote_ip_prefix="0.0.0.0/0"),
            ],
        )
        print(f"Security group: {sg.name} ({sg.id})")

        # Step 2. Attach to a running VM by name.
        client.cloud.instances.assign_security_group(
            instance_id=os.environ["INSTANCE_ID"],
            name="web-firewall",
        )
        print("Attached to VM.")

        # Step 3. Add an ICMP rule.
        client.cloud.security_groups.rules.create_and_poll(
            group_id=sg.id,
            direction="ingress",
            protocol="icmp",
            remote_ip_prefix="0.0.0.0/0",
        )
        print("ICMP rule added.")

        # Step 4. Detach and delete.
        client.cloud.instances.unassign_security_group(
            instance_id=os.environ["INSTANCE_ID"],
            name="web-firewall",
        )
        client.cloud.security_groups.delete(group_id=sg.id)
        print("Security group deleted.")
        ```
      </Tab>

      <Tab title="Go SDK">
        ```go theme={null}
        package main

        import (
            "context"
            "fmt"
            "log"
            "os"

            gcore "github.com/G-Core/gcore-go"
            "github.com/G-Core/gcore-go/cloud"
        )

        func main() {
            client := gcore.NewClient()
            ctx := context.Background()

            // Step 1. Create a security group with inbound rules.
            sg, err := client.Cloud.SecurityGroups.NewAndPoll(ctx, cloud.SecurityGroupNewParams{
                Name: "web-firewall",
                Rules: []cloud.SecurityGroupNewParamsRule{
                    {Direction: "ingress", Protocol: "tcp", PortRangeMin: gcore.Int(22), PortRangeMax: gcore.Int(22), RemoteIPPrefix: gcore.String("0.0.0.0/0")},
                    {Direction: "ingress", Protocol: "tcp", PortRangeMin: gcore.Int(80), PortRangeMax: gcore.Int(80), RemoteIPPrefix: gcore.String("0.0.0.0/0")},
                    {Direction: "ingress", Protocol: "tcp", PortRangeMin: gcore.Int(443), PortRangeMax: gcore.Int(443), RemoteIPPrefix: gcore.String("0.0.0.0/0")},
                },
            })
            if err != nil {
                log.Fatalf("create security group: %v", err)
            }
            fmt.Printf("Security group: %s (%s)\n", sg.Name, sg.ID)

            // Step 2. Attach to a running VM by name.
            if err = client.Cloud.Instances.AssignSecurityGroup(ctx, os.Getenv("INSTANCE_ID"), cloud.InstanceAssignSecurityGroupParams{
                Name: gcore.String("web-firewall"),
            }); err != nil {
                log.Fatalf("attach security group: %v", err)
            }
            fmt.Println("Attached to VM.")

            // Step 3. Add an ICMP rule.
            if _, err = client.Cloud.SecurityGroups.Rules.NewAndPoll(ctx, sg.ID, cloud.SecurityGroupRuleNewParams{
                Direction:      "ingress",
                Protocol:       "icmp",
                RemoteIPPrefix: gcore.String("0.0.0.0/0"),
            }); err != nil {
                log.Fatalf("add rule: %v", err)
            }
            fmt.Println("ICMP rule added.")

            // Step 4. Detach and delete.
            client.Cloud.Instances.UnassignSecurityGroup(ctx, os.Getenv("INSTANCE_ID"), cloud.InstanceUnassignSecurityGroupParams{
                Name: gcore.String("web-firewall"),
            })
            client.Cloud.SecurityGroups.Delete(ctx, sg.ID, cloud.SecurityGroupDeleteParams{})
            fmt.Println("Security group deleted.")
        }
        ```
      </Tab>
    </Tabs>

    <p>A security group starts empty for inbound traffic and automatically includes default egress rules that allow all outbound traffic. Rules can be passed at creation time to avoid separate calls.</p>

    ## Step-by-step

    <p>Each step below explains what the call does, which parameters matter, and what the response looks like. Use this section to understand the flow or to debug a specific step.</p>

    <Accordion title="Show all steps">
      ### Step 1. Create a security group

      A security group starts empty for inbound traffic and automatically includes default egress rules that allow all outbound traffic. Rules can be passed at creation time to avoid separate calls.

      | Parameter                           | Required       | Description                                                                     |
      | ----------------------------------- | -------------- | ------------------------------------------------------------------------------- |
      | `name`                              | Yes            | Used to attach the group to a running VM by name.                               |
      | `rules`                             | No             | Inbound or outbound rules to add at creation. Can also be added after creation. |
      | `direction`                         | Yes (per rule) | `"ingress"` for inbound, `"egress"` for outbound.                               |
      | `protocol`                          | No             | `"tcp"`, `"udp"`, `"icmp"`, or omit for all protocols. Lowercase only.          |
      | `port_range_min` / `port_range_max` | No             | Port range (1-65535). Omit for ICMP.                                            |
      | `remote_ip_prefix`                  | No             | Source CIDR. `"0.0.0.0/0"` allows all addresses.                                |

      <Tabs>
        <Tab title="Python SDK">
          ```python theme={null}
          import os
          from gcore import Gcore
          from gcore.types.cloud import security_group_create_params

          client = Gcore()

          sg = client.cloud.security_groups.create_and_poll(
              name="web-firewall",
              rules=[
                  security_group_create_params.Rule(direction="ingress", protocol="tcp", port_range_min=80, port_range_max=80, remote_ip_prefix="0.0.0.0/0"),
                  security_group_create_params.Rule(direction="ingress", protocol="tcp", port_range_min=443, port_range_max=443, remote_ip_prefix="0.0.0.0/0"),
                  security_group_create_params.Rule(direction="ingress", protocol="tcp", port_range_min=22, port_range_max=22, remote_ip_prefix="0.0.0.0/0"),
                  security_group_create_params.Rule(direction="ingress", protocol="icmp", remote_ip_prefix="0.0.0.0/0"),
              ],
          )
          print(f"Security group: {sg.name} ({sg.id})")
          ```
        </Tab>

        <Tab title="Go SDK">
          ```go theme={null}
          package main

          import (
              "context"
              "fmt"

              gcore "github.com/G-Core/gcore-go"
              "github.com/G-Core/gcore-go/cloud"
          )

          func main() {
              client := gcore.NewClient()

              sg, err := client.Cloud.SecurityGroups.NewAndPoll(context.TODO(), cloud.SecurityGroupNewParams{
                  Name: "web-firewall",
                  Rules: []cloud.SecurityGroupNewParamsRule{
                      {Direction: "ingress", Protocol: "tcp", PortRangeMin: gcore.Int(80), PortRangeMax: gcore.Int(80), RemoteIPPrefix: gcore.String("0.0.0.0/0")},
                      {Direction: "ingress", Protocol: "tcp", PortRangeMin: gcore.Int(443), PortRangeMax: gcore.Int(443), RemoteIPPrefix: gcore.String("0.0.0.0/0")},
                      {Direction: "ingress", Protocol: "tcp", PortRangeMin: gcore.Int(22), PortRangeMax: gcore.Int(22), RemoteIPPrefix: gcore.String("0.0.0.0/0")},
                      {Direction: "ingress", Protocol: "icmp", RemoteIPPrefix: gcore.String("0.0.0.0/0")},
                  },
              })
              if err != nil {
                  panic(err.Error())
              }
              fmt.Printf("Security group: %s (%s)\n", sg.Name, sg.ID)
          }
          ```
        </Tab>

        <Tab title="curl">
          ```bash theme={null}
          curl -X POST "https://api.gcore.com/cloud/v2/security_groups/$GCORE_CLOUD_PROJECT_ID/$GCORE_CLOUD_REGION_ID" \
            -H "Authorization: APIKey $GCORE_API_KEY" \
            -H "Content-Type: application/json" \
            -d '{
              "name": "web-firewall",
              "rules": [
                {"direction": "ingress", "protocol": "tcp", "port_range_min": 80, "port_range_max": 80, "ethertype": "IPv4", "remote_ip_prefix": "0.0.0.0/0"},
                {"direction": "ingress", "protocol": "tcp", "port_range_min": 443, "port_range_max": 443, "ethertype": "IPv4", "remote_ip_prefix": "0.0.0.0/0"},
                {"direction": "ingress", "protocol": "tcp", "port_range_min": 22, "port_range_max": 22, "ethertype": "IPv4", "remote_ip_prefix": "0.0.0.0/0"},
                {"direction": "ingress", "protocol": "icmp", "ethertype": "IPv4", "remote_ip_prefix": "0.0.0.0/0"}
              ]
            }'
          ```

          Response:

          ```json theme={null}
          {
            "tasks": ["d4a4b10e-..."]
          }
          ```

          Poll <code>GET /cloud/v1/tasks/{task_id}</code> every 5 seconds until `state` is `FINISHED`, then save the security group ID from `created_resources.security_groups[0]`:

          ```bash theme={null}
          export SG_ID="{SG_ID}"
          ```
        </Tab>
      </Tabs>

      A new security group is automatically populated with default egress rules that allow all outbound traffic. These do not need to be added manually. The [security groups API](/api-reference/cloud/security-groups) also supports listing all groups, updating rules in bulk, copying a group, and reverting to the default configuration.

      ### Step 2. Attach to a VM

      There are two ways to attach a security group: at VM creation or to a running instance.

      <Info>
        To attach to a running instance, pass the group **name** — not the UUID. The `assign_security_group` endpoint identifies groups by name.
      </Info>

      At VM creation, pass the security group UUID in the `security_groups` field:

      <Tabs>
        <Tab title="Python SDK">
          ```python theme={null}
          import os
          from gcore import Gcore

          client = Gcore()

          instance = client.cloud.instances.create_and_poll(
              name="my-vm",
              flavor=os.environ["FLAVOR_ID"],
              ssh_key_name="my-key",
              interfaces=[{"type": "external"}],
              volumes=[{"source": "image", "image_id": os.environ["IMAGE_ID"], "size": 20, "boot_index": 0}],
              security_groups=[{"id": os.environ["SG_ID"]}],
          )
          print(f"Instance: {instance.id}")
          ```
        </Tab>

        <Tab title="Go SDK">
          ```go theme={null}
          package main

          import (
              "context"
              "fmt"
              "os"

              gcore "github.com/G-Core/gcore-go"
              "github.com/G-Core/gcore-go/cloud"
          )

          func main() {
              client := gcore.NewClient()

              instance, err := client.Cloud.Instances.NewAndPoll(context.TODO(), cloud.InstanceNewParams{
                  Flavor:     os.Getenv("FLAVOR_ID"),
                  Name:       gcore.String("my-vm"),
                  SSHKeyName: gcore.String("my-key"),
                  Interfaces: []cloud.InstanceNewParamsInterfaceUnion{{
                      OfExternal: &cloud.InstanceNewParamsInterfaceExternal{},
                  }},
                  Volumes: []cloud.InstanceNewParamsVolumeUnion{{
                      OfImage: &cloud.InstanceNewParamsVolumeImage{
                          ImageID:   os.Getenv("IMAGE_ID"),
                          Size:      gcore.Int(20),
                          BootIndex: gcore.Int(0),
                      },
                  }},
                  SecurityGroups: []cloud.InstanceNewParamsSecurityGroup{{ID: os.Getenv("SG_ID")}},
              })
              if err != nil {
                  panic(err.Error())
              }
              fmt.Printf("Instance: %s\n", instance.ID)
          }
          ```
        </Tab>

        <Tab title="curl">
          ```bash theme={null}
          curl -X POST "https://api.gcore.com/cloud/v2/instances/$GCORE_CLOUD_PROJECT_ID/$GCORE_CLOUD_REGION_ID" \
            -H "Authorization: APIKey $GCORE_API_KEY" \
            -H "Content-Type: application/json" \
            -d '{
              "name": "my-vm",
              "flavor": "$FLAVOR_ID",
              "ssh_key_name": "my-key",
              "interfaces": [{"type": "external"}],
              "volumes": [{"source": "image", "image_id": "'"$IMAGE_ID"'", "size": 20, "boot_index": 0}],
              "security_groups": [{"id": "'"$SG_ID"'"}]
            }'
          ```
        </Tab>
      </Tabs>

      To a running instance, use the group name:

      <Tabs>
        <Tab title="Python SDK">
          ```python theme={null}
          import os
          from gcore import Gcore

          client = Gcore()

          client.cloud.instances.assign_security_group(
              instance_id=os.environ["INSTANCE_ID"],
              name="web-firewall",
          )
          print("Security group attached.")
          ```
        </Tab>

        <Tab title="Go SDK">
          ```go theme={null}
          package main

          import (
              "context"
              "fmt"

              gcore "github.com/G-Core/gcore-go"
              "github.com/G-Core/gcore-go/cloud"
          )

          func main() {
              client := gcore.NewClient()

              err := client.Cloud.Instances.AssignSecurityGroup(context.TODO(), os.Getenv("INSTANCE_ID"), cloud.InstanceAssignSecurityGroupParams{
                  Name: gcore.String("web-firewall"),
              })
              if err != nil {
                  panic(err.Error())
              }
              fmt.Println("Security group attached.")
          }
          ```
        </Tab>

        <Tab title="curl">
          ```bash theme={null}
          curl -X POST \
            "https://api.gcore.com/cloud/v1/instances/$GCORE_CLOUD_PROJECT_ID/$GCORE_CLOUD_REGION_ID/$INSTANCE_ID/addsecuritygroup" \
            -H "Authorization: APIKey $GCORE_API_KEY" \
            -H "Content-Type: application/json" \
            -d '{"name": "web-firewall"}'
          ```
        </Tab>
      </Tabs>

      ### Step 3. Add a rule

      To add a rule after the security group is created, call the [rules endpoint](/api-reference/cloud/security-groups/create-security-group-rule) with the group ID. A security group supports up to 250 rules total across inbound and outbound directions.

      <Tabs>
        <Tab title="Python SDK">
          ```python theme={null}
          import os
          from gcore import Gcore

          client = Gcore()

          rule = client.cloud.security_groups.rules.create_and_poll(
              group_id=os.environ["SG_ID"],
              direction="ingress",
              protocol="tcp",
              port_range_min=8080,
              port_range_max=8080,
              remote_ip_prefix="0.0.0.0/0",
          )
          print(f"Rule added: {rule.id}")
          ```
        </Tab>

        <Tab title="Go SDK">
          ```go theme={null}
          package main

          import (
              "context"
              "fmt"

              gcore "github.com/G-Core/gcore-go"
              "github.com/G-Core/gcore-go/cloud"
          )

          func main() {
              client := gcore.NewClient()

              rule, err := client.Cloud.SecurityGroups.Rules.NewAndPoll(context.TODO(), os.Getenv("SG_ID"), cloud.SecurityGroupRuleNewParams{
                  Direction:      "ingress",
                  Protocol:       "tcp",
                  PortRangeMin:   gcore.Int(8080),
                  PortRangeMax:   gcore.Int(8080),
                  RemoteIPPrefix: gcore.String("0.0.0.0/0"),
              })
              if err != nil {
                  panic(err.Error())
              }
              fmt.Printf("Rule added: %s\n", rule.ID)
          }
          ```
        </Tab>

        <Tab title="curl">
          ```bash theme={null}
          curl -X POST \
            "https://api.gcore.com/cloud/v2/security_groups/$GCORE_CLOUD_PROJECT_ID/$GCORE_CLOUD_REGION_ID/$SG_ID/rules" \
            -H "Authorization: APIKey $GCORE_API_KEY" \
            -H "Content-Type: application/json" \
            -d '{
              "direction": "ingress",
              "protocol": "tcp",
              "port_range_min": 8080,
              "port_range_max": 8080,
              "ethertype": "IPv4",
              "remote_ip_prefix": "0.0.0.0/0"
            }'
          ```

          Response:

          ```json theme={null}
          {
            "tasks": ["a7b3c9e1-..."]
          }
          ```
        </Tab>
      </Tabs>

      <Warning>
        When multiple security groups are assigned to a Virtual Machine, rules from all groups are evaluated together.
      </Warning>
    </Accordion>

    ## Clean up

    <p>Detach the security group from all instances before deleting it, otherwise the delete request returns an error.</p>

    <Tabs>
      <Tab title="Python SDK">
        ```python theme={null}
        import os
        from gcore import Gcore

        client = Gcore()

        client.cloud.instances.unassign_security_group(
            instance_id=os.environ["INSTANCE_ID"],
            name="web-firewall",
        )
        client.cloud.security_groups.delete(group_id=os.environ["SG_ID"])
        ```
      </Tab>

      <Tab title="Go SDK">
        ```go theme={null}
        package main

        import (
            "context"
            "os"

            gcore "github.com/G-Core/gcore-go"
            "github.com/G-Core/gcore-go/cloud"
        )

        func main() {
            client := gcore.NewClient()

            client.Cloud.Instances.UnassignSecurityGroup(context.TODO(), os.Getenv("INSTANCE_ID"), cloud.InstanceUnassignSecurityGroupParams{
                Name: gcore.String("web-firewall"),
            })
            client.Cloud.SecurityGroups.Delete(context.TODO(), os.Getenv("SG_ID"), cloud.SecurityGroupDeleteParams{})
        }
        ```
      </Tab>

      <Tab title="curl">
        ```bash theme={null}
        # Detach from VM
        curl -X POST \
          "https://api.gcore.com/cloud/v1/instances/$GCORE_CLOUD_PROJECT_ID/$GCORE_CLOUD_REGION_ID/$INSTANCE_ID/delsecuritygroup" \
          -H "Authorization: APIKey $GCORE_API_KEY" \
          -H "Content-Type: application/json" \
          -d '{"name": "web-firewall"}'

        # Delete the security group
        curl -X DELETE \
          "https://api.gcore.com/cloud/v1/securitygroups/$GCORE_CLOUD_PROJECT_ID/$GCORE_CLOUD_REGION_ID/$SG_ID" \
          -H "Authorization: APIKey $GCORE_API_KEY"
        ```
      </Tab>
    </Tabs>
  </MethodSection>

  <MethodSection id="terraform" label="Terraform">
    <p>Declare a security group with ingress rules using [`gcore_cloud_security_group`](https://registry.terraform.io/providers/G-Core/gcore/latest/docs/resources/cloud_security_group). Rules are managed as separate `gcore_cloud_security_group_rule` resources and can be added or removed independently.</p>

    ## Create a security group

    <p>Declares a security group and its inbound rules as separate resources. Each rule references the group via `group_id` — default egress rules allowing all outbound traffic are added automatically.</p>

    ```hcl theme={null}
    resource "gcore_cloud_security_group" "example" {
      project_id = var.project_id
      region_id  = var.region_id
      name       = "web-firewall"
    }

    resource "gcore_cloud_security_group_rule" "allow_ssh" {
      project_id       = var.project_id
      region_id        = var.region_id
      group_id         = gcore_cloud_security_group.example.id
      direction        = "ingress"
      ethertype        = "IPv4"
      protocol         = "tcp"
      port_range_min   = 22
      port_range_max   = 22
      remote_ip_prefix = "0.0.0.0/0"
    }

    resource "gcore_cloud_security_group_rule" "allow_http" {
      project_id       = var.project_id
      region_id        = var.region_id
      group_id         = gcore_cloud_security_group.example.id
      direction        = "ingress"
      ethertype        = "IPv4"
      protocol         = "tcp"
      port_range_min   = 80
      port_range_max   = 80
      remote_ip_prefix = "0.0.0.0/0"
    }

    resource "gcore_cloud_security_group_rule" "allow_https" {
      project_id       = var.project_id
      region_id        = var.region_id
      group_id         = gcore_cloud_security_group.example.id
      direction        = "ingress"
      ethertype        = "IPv4"
      protocol         = "tcp"
      port_range_min   = 443
      port_range_max   = 443
      remote_ip_prefix = "0.0.0.0/0"
    }

    output "security_group_id" {
      value = gcore_cloud_security_group.example.id
    }
    ```

    ## Attach to a VM

    <p>Reference the security group in a [Virtual Machine](/cloud/virtual-instances/create-an-instance) resource. The `security_groups` block applies the group to all VM network interfaces.</p>

    ```hcl theme={null}
    resource "gcore_cloud_instance" "example" {
      project_id = var.project_id
      region_id  = var.region_id
      # ... other instance fields ...

      security_groups = [{
        id = gcore_cloud_security_group.example.id
      }]
    }
    ```

    ## Add a rule

    <p>Add a new `gcore_cloud_security_group_rule` block and run `terraform apply`.</p>

    ```hcl theme={null}
    resource "gcore_cloud_security_group" "example" { ... }          # unchanged
    resource "gcore_cloud_security_group_rule" "allow_ssh" { ... }   # unchanged
    resource "gcore_cloud_security_group_rule" "allow_http" { ... }  # unchanged
    resource "gcore_cloud_security_group_rule" "allow_https" { ... } # unchanged

    resource "gcore_cloud_security_group_rule" "allow_icmp" {
      project_id       = var.project_id
      region_id        = var.region_id
      group_id         = gcore_cloud_security_group.example.id
      direction        = "ingress"
      ethertype        = "IPv4"
      protocol         = "icmp"
      remote_ip_prefix = "0.0.0.0/0"
    }
    ```

    ```bash theme={null}
    terraform apply
    ```

    ## Remove a rule

    <p>Remove the rule block — Terraform detects the missing declaration and deletes the rule on the next `terraform apply`.</p>

    ```hcl theme={null}
    # Remove or comment out the rule to delete:
    # resource "gcore_cloud_security_group_rule" "allow_http" {
    #   project_id       = var.project_id
    #   region_id        = var.region_id
    #   group_id         = gcore_cloud_security_group.example.id
    #   direction        = "ingress"
    #   ethertype        = "IPv4"
    #   protocol         = "tcp"
    #   port_range_min   = 80
    #   port_range_max   = 80
    #   remote_ip_prefix = "0.0.0.0/0"
    # }
    ```

    ```bash theme={null}
    terraform apply
    ```

    ## Delete a security group

    <p>Remove the security group block and all its rule blocks — Terraform deletes the rules first, then the group.</p>

    ```hcl theme={null}
    # Remove or comment out all blocks:
    # resource "gcore_cloud_security_group" "example" { ... }
    # resource "gcore_cloud_security_group_rule" "allow_ssh" { ... }
    # resource "gcore_cloud_security_group_rule" "allow_http" { ... }
    # resource "gcore_cloud_security_group_rule" "allow_https" { ... }
    ```

    ```bash theme={null}
    terraform apply
    ```

    ## Import a security group

    <p>Use when a security group was created outside Terraform and needs to be managed as code.</p>

    ```hcl theme={null}
    resource "gcore_cloud_security_group" "example" {
      project_id = var.project_id
      region_id  = var.region_id
      name       = "web-firewall"
    }
    ```

    ```bash theme={null}
    terraform import gcore_cloud_security_group.example '<project_id>/<region_id>/<group_id>'
    ```
  </MethodSection>
</MethodSwitch>
