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

# OCI Compute Instances Should Not Have Public IP Addresses

### More Info:

Compute instances should not have public IP addresses assigned. Public IPs expose instances directly to internet-based attacks and should be avoided in favor of load balancers or bastion hosts.

### Risk Level

Critical

### Address

Compliance, Security

### Compliance Standards

* APRA CPS 234 (Australia)
* AWS Startup Security Baseline
* AWS Well Architected Framework
* BSI C5 (Germany)
* Brazil LGPD
* CCPA / CPRA (California)
* CIS Critical Security Controls v8
* CMMC 2.0
* CSA Cloud Controls Matrix v4
* DPDPA
* Digital Operational Resilience Act (EU)
* FedRAMP
* HIPAA
* HITRUST CSF
* ISO/IEC 27017
* ISO/IEC 27018
* ISO/IEC 27701
* KSA PDPL
* MAS Technology Risk Management (Singapore)
* MITRE ATT\&CK (Cloud)
* NIS2 Directive
* NIST CSF
* NIST SP 800-171
* NYDFS 23 NYCRR 500
* PCI
* Reserve Bank of India (RBI) Cyber Security Framework
* SOC2
* SWIFT Customer Security Controls Framework
* Sarbanes-Oxley IT General Controls
* Securities and Exchange Board of India (SEBI) - Cloud Security Adoption Framework
* UK NCSC Cyber Assessment Framework

### Triage and Remediation

<Tabs>
  <Tab title="Remediation">
    ### Remediation

    <AccordionGroup>
      <Accordion title="Using Console" defaultOpen="true">
        Below are step‑by‑step console instructions to remediate “OCI Compute Instances Should Not Have Public IP Addresses.”

        ## 1. Identify Instances With Public IPs

        1. Sign in to OCI Console.
        2. Choose the correct **Region** (top right).
        3. Open the navigation menu → **Compute** → **Instances**.
        4. Select the **Compartment** where your instances reside.
        5. For each instance:
           * Click the instance name.
           * Under **Resources** (left side), click **Attached VNICs**.
           * Click the primary VNIC.
           * In the VNIC details, check:
             * **Public IP** field
             * If present, note the **Public IP address** and whether it’s **ephemeral** or **reserved**.

        You will remediate each instance/VNIC that has a Public IP.

        ***

        ## 2. Remove Public IP From the Instance (VNIC)

        > You cannot detach a primary VNIC, but you can remove or change its Public IP.\
        > For safety, **stop the instance** if it’s production and you want to avoid connection issues during change.

        ### 2.1 (Optional but recommended) Stop the Instance

        1. On the instance details page, click **Stop**.
        2. Wait until the state is **Stopped**.

        ### 2.2 Remove or Change the Public IP

        1. On the same instance page, under **Resources**, click **Attached VNICs**.
        2. Click the primary VNIC (usually named something like `instance-name (Primary VNIC)`).
        3. In the VNIC details page:
           * Locate the **IPv4 Addresses** section.
           * Find the **Primary private IP** row.
           * In the **Public IP** column:
             * Click the **Actions** menu (three dots) or the link for the public IP.

        Depending on the type:

        #### If it’s an Ephemeral Public IP:

        1. In the **Public IP** details, click **Edit** or **Actions** → **Unassign** / **Remove**.
        2. Confirm the unassignment.
           * After this, the Primary private IP will show **No public IP address**.

        #### If it’s a Reserved Public IP:

        1. In the **Public IP** details, click **Edit** or **Actions** → **Unassign**.
        2. Confirm.
        3. (Optional cleanup) If you no longer need that reserved IP:
           * Go to navigation menu → **Networking** → **Public IPs**.
           * Choose the correct Compartment.
           * Find that **Reserved Public IP**, click its name.
           * Click **Release Public IP** to return it to the pool.

        ### 2.3 Start the Instance (if you stopped it)

        1. Return to the instance page.
        2. Click **Start**.
        3. Confirm that applications are reachable via private paths (VPN, FastConnect, Bastion, etc.).

        ***

        ## 3. Prevent Future Public IP Assignment at Subnet Level

        To avoid new instances in that subnet getting public IPs:

        1. Open navigation menu → **Networking** → **Virtual Cloud Networks**.
        2. Select the VCN where the instance’s subnet resides.
        3. Under **Resources**, click **Subnets**.
        4. Click the subnet that was used by your instance.
        5. Click **Edit**.
        6. Set:
           * **Public IP address**:
             * Uncheck or choose option that **disables public IP assignment** (wording may be “Do not assign public IPv4 addresses” or similar).
        7. Click **Save changes**.

        New instances or VNICs in this subnet will not be allowed to have public IPs.

        ***

        ## 4. (Optional) Use OCI Bastion for Private-Only Access

        If you used the public IP for SSH/RDP:

        1. Open navigation menu → **Identity & Security** → **Bastion**.
        2. Create a **Bastion** in:
           * The same **Region**.
           * A **public subnet** with proper security list/NSG rules.
        3. Configure a **Session** (SSH, RDP, etc.) from your workstation through the Bastion to the **private IP** of your instance.
        4. Confirm you can connect without needing a public IP on the instance.

        ***

        ## 5. Confirm Remediation for Monitoring/Compliance

        To ensure OCI monitoring/compliance tools recognize the remediation:

        1. Wait for the next evaluation cycle (usually a few minutes up to an hour, depending on the security/monitoring service).
        2. In your OCI **Security/Monitoring** or **Cloud Guard** dashboards, confirm that:
           * The problem “Compute Instances Should Not Have Public IP Addresses” for those instances is either:
             * Marked **Resolved**, or
             * No longer shown as active.

        You have now removed public IP addresses from your OCI Compute instances and hardened the subnet to prevent new public IPs from being assigned.
      </Accordion>

      <Accordion title="Using CLI">
        Below is a focused, step‑by‑step way to remediate “OCI Compute Instances Should Not Have Public IP Addresses” using the OCI CLI. The core actions are:

        1. Identify instances with public IPs
        2. Remove public IPs from their VNICs
        3. Prevent new public IP assignment at subnet level

        ***

        ## 1. Prerequisites

        * OCI CLI installed and configured (`oci setup config`)
        * You know:
          * Your `compartment-ocid`
          * The region you’re working in (`--region` if different from default)

        ***

        ## 2. Identify Compute Instances with Public IPs

        ### 2.1. List instances in a compartment

        ```bash theme={null}
        oci compute instance list \
          --compartment-id <compartment-ocid> \
          --all \
          --query "data[?\"lifecycle-state\"=='RUNNING' || \"lifecycle-state\"=='STOPPED'].{id:id,\"display-name\":\"display-name\"}" \
          --output table
        ```

        For each instance, get its VNICs and public IPs.

        ### 2.2. For each instance, get its VNIC attachments

        ```bash theme={null}
        INSTANCE_OCID=<instance-ocid>

        oci compute vnic-attachment list \
          --compartment-id <compartment-ocid> \
          --instance-id $INSTANCE_OCID \
          --all \
          --query "data[].{\"id\":id,\"vnic-id\":\"vnic-id\"}" \
          --output table
        ```

        ### 2.3. For each VNIC, check if it has a public IP

        ```bash theme={null}
        VNIC_ID=<vnic-ocid>

        oci network vnic get \
          --vnic-id $VNIC_ID \
          --query "data.{\"vnic-id\":id,\"public-ip\":\"public-ip\",\"is-primary\":\"is-primary\"}" \
          --output table
        ```

        If `public-ip` is not `null`, that VNIC currently has a public IP.

        ***

        ## 3. Remove Public IP Addresses from VNICs

        There are two types of public IPs: ephemeral and reserved. You unassign them differently.

        ### 3.1. Determine the public IP object (ephemeral vs reserved)

        Use the VNIC’s `public-ip` from the previous step (IP address string).

        ```bash theme={null}
        IP_ADDRESS=<public-ip-address>

        oci network public-ip list \
          --compartment-id <compartment-ocid> \
          --all \
          --query "data[?\"ip-address\"=='$IP_ADDRESS']"
        ```

        If it returns a resource with an OCID and `"lifetime": "RESERVED"`, it’s a reserved public IP.\
        If it doesn’t return anything, it’s likely an ephemeral public IP associated directly to the VNIC.

        ***

        ### 3.2. If the public IP is ephemeral (most common)

        Use `vnic update` to remove the public IP:

        ```bash theme={null}
        oci network vnic update \
          --vnic-id $VNIC_ID \
          --assign-public-ip false
        ```

        This detaches the ephemeral public IP from the VNIC.

        ***

        ### 3.3. If the public IP is reserved

        First, unassign the reserved public IP from the VNIC, then optionally delete it.

        #### 3.3.1. Find the reserved public IP OCID

        ```bash theme={null}
        oci network public-ip list \
          --compartment-id <compartment-ocid> \
          --all \
          --query "data[?\"ip-address\"=='$IP_ADDRESS'].id | [0]" \
          --raw-output
        ```

        Save it:

        ```bash theme={null}
        PUBLIC_IP_ID=<public-ip-ocid>
        ```

        #### 3.3.2. Unassign the reserved public IP from the VNIC

        ```bash theme={null}
        oci network public-ip update \
          --public-ip-id $PUBLIC_IP_ID \
          --private-ip-id "" \
          --vnic-id ""
        ```

        Some older API patterns require you to update the `private-ip-id` to null (or assign to a different private IP). If the above fails, you can:

        1. Get the private IP behind the VNIC:
           ```bash theme={null}
           oci network private-ip list \
             --vnic-id $VNIC_ID \
             --query "data[?\"is-primary\"==\`true\`].id | [0]" \
             --raw-output
           ```
        2. Use `oci network public-ip update` to dissociate or reassign as required (depends on tenancy policy and version). If removing directly is not allowed, you may need to reassign to some “holding” private IP that is not used by a compute instance.

        #### 3.3.3. Optionally delete the reserved public IP (to avoid reuse)

        ```bash theme={null}
        oci network public-ip delete \
          --public-ip-id $PUBLIC_IP_ID \
          --force
        ```

        ***

        ## 4. Prevent Future Public IP Assignment at Subnet Level

        To ensure instances in a subnet cannot get public IPs:

        ### 4.1. Get subnet details and current setting

        ```bash theme={null}
        SUBNET_ID=<subnet-ocid>

        oci network subnet get \
          --subnet-id $SUBNET_ID \
          --query "data.{id:id,\"display-name\":\"display-name\",\"prohibit-public-ip-on-vnic\":\"prohibit-public-ip-on-vnic\"}" \
          --output table
        ```

        If `prohibit-public-ip-on-vnic` is `false`, change it to `true`.

        ### 4.2. Update the subnet

        ```bash theme={null}
        oci network subnet update \
          --subnet-id $SUBNET_ID \
          --prohibit-public-ip-on-vnic true \
          --force
        ```

        From now on, new VNICs in this subnet cannot have public IPs. Existing VNICs with public IPs are not automatically stripped; you must remediate them as in steps 2–3.

        ***

        ## 5. Bulk / Scripted Remediation Pattern (Optional)

        To remediate all instances in a compartment:

        1. List all VNICs that currently have a `public-ip`:
           ```bash theme={null}
           oci compute instance list --compartment-id <compartment-ocid> --all \
           | jq -r '.data[].id' \
           | while read INSTANCE_ID; do
               oci compute vnic-attachment list --compartment-id <compartment-ocid> --instance-id "$INSTANCE_ID" --all \
               | jq -r '.data[]."vnic-id"' \
               | while read VNIC_ID; do
                   oci network vnic get --vnic-id "$VNIC_ID" \
                   | jq -r '.data | select(.["public-ip"] != null) | ."public-ip"' \
                   | while read IP; do
                       echo "Removing public IP $IP from VNIC $VNIC_ID (instance $INSTANCE_ID)"
                       oci network vnic update --vnic-id "$VNIC_ID" --assign-public-ip false
                   done
                 done
             done
           ```

        2. Then update subnets as in section 4 to prevent re‑introduction.

        ***

        If you share your compartment OCID / subnet structure (sanitized), I can help format exact ready‑to‑run commands for your layout.
      </Accordion>

      <Accordion title="Using Python">
        Below is a concise, Python‑based approach to **detect and remediate** OCI Compute instances that have public IPs by unassigning those IPs from their VNICs.

        Assumptions:

        * You have `oci` Python SDK installed (`pip install oci`).
        * Your `~/.oci/config` is set up, or you otherwise pass config programmatically.
        * You understand that unassigning public IPs may break connectivity (SSH/RDP) to those instances.

        ***

        ## 1. Install and Import OCI SDK

        ```bash theme={null}
        pip install oci
        ```

        ```python theme={null}
        import oci
        from oci.core import ComputeClient, VirtualNetworkClient
        ```

        ***

        ## 2. Create OCI Clients

        ```python theme={null}
        config = oci.config.from_file("~/.oci/config", "DEFAULT")
        compute_client = ComputeClient(config)
        vcn_client = VirtualNetworkClient(config)

        compartment_id = "<your_compartment_ocid>"
        ```

        ***

        ## 3. List Instances and Their VNICs

        ```python theme={null}
        # Get all instances in the compartment
        instances = oci.pagination.list_call_get_all_results(
            compute_client.list_instances,
            compartment_id=compartment_id,
            lifecycle_state="RUNNING"  # or omit to include all
        ).data
        ```

        Now get each instance’s VNIC attachments:

        ```python theme={null}
        vnic_attachments = []
        for instance in instances:
            vas = oci.pagination.list_call_get_all_results(
                compute_client.list_vnic_attachments,
                compartment_id=compartment_id,
                instance_id=instance.id
            ).data
            vnic_attachments.extend(vas)
        ```

        ***

        ## 4. Detect VNICs with Public IPs

        ```python theme={null}
        vnics_with_public_ip = []

        for va in vnic_attachments:
            vnic = vcn_client.get_vnic(va.vnic_id).data
            if vnic.public_ip:
                vnics_with_public_ip.append((vnic, va))
        ```

        At this point you have all VNICs with public IPs.

        ***

        ## 5. Unassign the Public IPs

        There are two cases in OCI:

        1. **Ephemeral Public IPs** (assigned directly to VNIC).
        2. **Reserved Public IPs** (separate OCI resource, associated with a private IP).

        ### 5.1. Unassign Ephemeral Public IP

        ```python theme={null}
        for vnic, va in vnics_with_public_ip:
            # For ephemeral public IPs, the public IP is directly tied to the VNIC primary private IP.
            # Get the private IP resource:
            private_ips = oci.pagination.list_call_get_all_results(
                vcn_client.list_private_ips,
                vnic_id=vnic.id
            ).data

            for priv_ip in private_ips:
                # if this private IP has an associated public IP (ephemeral)
                if priv_ip.public_ip:
                    print(f"Unassigning ephemeral public IP from VNIC {vnic.id} (Instance: {va.instance_id})")
                    # Update private IP to remove public IP
                    update_details = oci.core.models.UpdatePrivateIpDetails(
                        defined_tags=priv_ip.defined_tags,
                        display_name=priv_ip.display_name,
                        freeform_tags=priv_ip.freeform_tags,
                        hostname_label=priv_ip.hostname_label
                        # No explicit field to “unset” public_ip; we remove via UpdatePublicIp or delete reserved IP
                    )
                    # In practice, ephemeral IPs are deleted by deleting/ resetting the association:
                    # For ephemeral public IPs, you usually call:
                    # vcn_client.update_public_ip(...) only for reserved; for ephemeral, you use DeletePublicIp when exposed.
                    # OCI exposes ephemeral public IPs via get_public_ip_by_ip_address for deletion.
                    public_ip_obj = vcn_client.get_public_ip_by_ip_address(
                        get_public_ip_by_ip_address_details=oci.core.models.GetPublicIpByIpAddressDetails(
                            ip_address=priv_ip.public_ip
                        )
                    ).data

                    # Delete (unassign) the public IP
                    vcn_client.delete_public_ip(public_ip_obj.id)
        ```

        > Note: For **ephemeral** public IPs, they are modeled as a `PublicIp` resource of type `EPHEMERAL`. Deleting that `PublicIp` unassigns it from the private IP.

        ### 5.2. Unassign Reserved Public IPs

        Reserved public IPs may be attached to specific private IPs. Detection is similar; identify the `PublicIp` resource and delete or “unassign” it.

        ```python theme={null}
        # List all reserved public IPs in compartment, then detach them
        public_ips = oci.pagination.list_call_get_all_results(
            vcn_client.list_public_ips,
            compartment_id=compartment_id,
            scope="REGION"
        ).data

        for pub_ip in public_ips:
            if pub_ip.lifecycle_state != "ASSIGNED":
                continue

            # If you want to remove all instance public IPs:
            if pub_ip.assigned_entity_id:  # usually the private IP OCID
                print(f"Unassigning reserved public IP {pub_ip.ip_address} ({pub_ip.id})")
                # Delete reserved public IP – this unassigns it
                vcn_client.delete_public_ip(pub_ip.id)
        ```

        ***

        ## 6. Wrap into a “Remediation Script”

        Combine the logic into a script that:

        1. Lists instances and their VNICs.
        2. Finds all VNICS/private IPs with public IPs.
        3. Resolves the corresponding `PublicIp` resources.
        4. Deletes (unassigns) those `PublicIp` resources.

        Skeleton:

        ```python theme={null}
        import oci

        def remove_public_ips_from_instances(compartment_id, config_profile="DEFAULT"):
            config = oci.config.from_file("~/.oci/config", config_profile)
            compute_client = oci.core.ComputeClient(config)
            vcn_client = oci.core.VirtualNetworkClient(config)

            instances = oci.pagination.list_call_get_all_results(
                compute_client.list_instances,
                compartment_id=compartment_id,
                lifecycle_state="RUNNING"
            ).data

            for instance in instances:
                vas = oci.pagination.list_call_get_all_results(
                    compute_client.list_vnic_attachments,
                    compartment_id=compartment_id,
                    instance_id=instance.id
                ).data

                for va in vas:
                    vnic = vcn_client.get_vnic(va.vnic_id).data
                    if not vnic.public_ip:
                        continue

                    private_ips = oci.pagination.list_call_get_all_results(
                        vcn_client.list_private_ips,
                        vnic_id=vnic.id
                    ).data

                    for priv_ip in private_ips:
                        if not priv_ip.public_ip:
                            continue

                        # Get the public IP resource
                        public_ip_obj = vcn_client.get_public_ip_by_ip_address(
                            get_public_ip_by_ip_address_details=oci.core.models.GetPublicIpByIpAddressDetails(
                                ip_address=priv_ip.public_ip
                            )
                        ).data

                        print(f"Unassigning public IP {public_ip_obj.ip_address} from instance {instance.display_name} ({instance.id})")
                        vcn_client.delete_public_ip(public_ip_obj.id)

        if __name__ == "__main__":
            remove_public_ips_from_instances("<your_compartment_ocid>")
        ```

        ***

        ## 7. (Optional) Monitoring / Alerting

        To integrate this with “compute monitoring”:

        1. Use **OCI Monitoring / Alarms** or **Cloud Guard** to detect instances with public IPs (e.g., via IAM/Config rules or Cloud Guard detector).
        2. Configure an **Alarm** or **Cloud Guard Responder** that triggers a **Notification** (OCI Events + Functions or OCI Notifications to a function).
        3. That Function can run the Python logic above (packaged into a function) to automatically remediate.

        If you specify whether you want a standalone script, OCI Function code, or integration with Cloud Guard/Events, I can give a tailored Python example for that environment.
      </Accordion>

      <Accordion title="Using Terraform">
        ```hcl theme={null}
        resource "oci_core_instance" "COMPUTE_INSTANCE_NAME" {
          # Replace with your compartment and availability details
          compartment_id      = "OCID_OF_COMPARTMENT"
          availability_domain = "AVAILABILITY_DOMAIN_NAME"
          shape               = "VM.SHAPE_NAME"
          display_name        = "COMPUTE_INSTANCE_NAME"

          source_details {
            source_type = "image"
            source_id   = "OCID_OF_IMAGE"
          }

          create_vnic_details {
            subnet_id        = "OCID_OF_SUBNET"
            hostname_label   = "HOSTNAME_LABEL"
            assign_public_ip = "false" # Ensure no public IP is assigned
          }

          # ... any other required arguments ...
        }
        ```

        Setting `assign_public_ip = "false"` (or omitting it when the subnet is private-by-default) prevents the instance from receiving a public IP; changing this on an existing instance forces replacement (destroy and recreate) of the instance/VNIC in Terraform.

        After the change, `terraform plan` should show the `assign_public_ip` argument changing from `"true"` (or `"yes"`) to `"false"` with a planned `-/+` replacement for the `oci_core_instance` resource.
      </Accordion>
    </AccordionGroup>
  </Tab>
</Tabs>
