feat(cli): VPC support for the AWS installer - #17
Closed
kylehounslow wants to merge 5 commits into
Closed
Conversation
Add VPC support to the aws/cli-installer advanced deploy path. New flags --vpc-id, --subnet-ids, and --security-group-ids (plus an interactive Network topology step) place the OpenSearch domain, OSIS ingestion pipeline, and EC2 demo instance into a VPC with private endpoints. - Domain: sets VPCOptions and enables zone awareness across the provided subnets/AZs, resolving the VPC-private endpoint from Endpoints.vpc. - OSIS pipeline: attaches VpcOptions (first two subnets) so it can reach the private domain. - EC2 demo: launches into the selected subnet and reuses the provided security groups, so the otel-demo lands in the same VPC/subnet/SG. FGAC role mapping and OpenSearch UI setup do NOT require the installer to run inside the VPC. The managed OpenSearch UI (Application) endpoint is public and proxies to the domain over the AWS-internal network, so for VPC-private domains the installer: - sets the caller's IAM principal as the domain master (so SigV4 is authorized without an in-VPC basic-auth bootstrap), - authorizes the OpenSearch UI service principal (application.opensearchservice.amazonaws.com) to reach the domain through its VPC endpoint, and - performs FGAC role mapping through the reachable Application endpoint. Omitting --vpc-id preserves the current public-endpoint behavior (internal-database master, direct-to-domain role mapping). Validation, README docs, and unit tests for the new options included. A zone-aware domain requires the data-node count to be a multiple of the AZ count; the installer rounds up and reports it rather than failing.
Make the installer deterministic so a fresh deploy always completes: - Gate domain readiness on Processing clearing, not just endpoint presence. The endpoint URL is published while the cluster is still initializing, which raced the immediately-following security-API calls (FGAC mapping, UI-to-domain connection). - Wait for the OpenSearch Application endpoint after CreateApplication. The endpoint is provisioned after the create call returns, so a single read got an empty value and silently skipped FGAC role mapping and UI setup for VPC domains. - Retry FGAC role mapping (both the direct basic-auth path and the VPC OpenSearch-UI path) on transient 5xx / connection errors and "No Living connections", and fail hard if it never succeeds. A silent miss left the pipeline ACTIVE but unable to write. - Retry OSIS CreatePipeline and AddDirectQueryDataSource on IAM role-not-propagated errors instead of a fixed sleep. - Validate that an OpenSearch backend is selected up front. Advanced mode with only VPC flags left osAction empty, skipped domain creation, and failed deep in pipeline creation with an empty host. Adds unit tests for the retry helper, error classifiers, and the backend-required guard.
kylehounslow
marked this pull request as draft
July 22, 2026 15:04
Add validateVpcTopology() which uses the EC2 Describe APIs to fail fast on invalid VPC deployment inputs, before any OpenSearch/OSIS resources are created: - VPC exists in the target region (clean error on InvalidVpcID.NotFound) - every subnet and security group is a member of the given VPC - subnets land in distinct AZs, matching the zone-awareness config that createOpenSearch() derives from the subnet count (a shared AZ otherwise fails deep in CreateDomain) Wired into executePipeline() right after checkRequirements(), so both the flag-driven and interactive paths validate before creation. Accessors are injectable; adds 11 unit tests covering each branch.
The ASCII architecture diagram shown before the deploy confirmation was identical whether or not a VPC was configured, so the network boundary this feature introduces was invisible at the point the operator commits. - Tag the boxes that run inside the VPC ([vpc] on EC2, OSI endpoint, and the OpenSearch domain). Prometheus, the Connected Data Source, and the UI are regional managed services and stay untagged. - Prepend a network-topology header in VPC mode with the VPC, subnet, and security-group IDs, and a note that the regional services are reached over the AWS-internal path. - The tag fits inside the boxes' existing minimum widths, so every box width and column position is unchanged from the public render. Exports renderArchitectureDiagram and adds its first unit tests: public render has no tags/header, VPC render carries the header IDs and tags the three private boxes, and a regression check that box widths match the public render so the positional layout can't drift.
The header above the diagram was easy to scroll past. Placing it after the drawing reads as a caption on what was just rendered.
kylehounslow
force-pushed
the
feat/vpc-support
branch
from
July 22, 2026 21:52
c65fff2 to
c9ead4b
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
VPC support for the AWS CLI installer
Closes opensearch-project#331.
Adds an opt-in VPC deployment path to the
aws/cli-installer. When you pass--vpc-id(with--subnet-idsand--security-group-ids), the OpenSearch domain, the OSIS ingestion pipeline, and the EC2 demo instance are all placed inside the selected VPC on private endpoints. Omitting--vpc-idleaves the current public-endpoint path unchanged.What changed
--vpc-id,--subnet-ids,--security-group-ids(comma-separated), plus a Network topology step in interactive advanced mode.VPCOptionsacross the given subnets; zone-aware when more than one subnet (AZ) is provided, rounding the data-node count up to a multiple of the AZ count.validateVpcTopology()calls the EC2 Describe APIs to confirm the VPC exists in the region, every subnet and security group belongs to it, and the subnets sit in distinct AZs (the zone-awareness rule the domain config derives from the subnet count). Wrong network inputs fail in seconds with a clear message instead of minutes into domain or pipeline creation, and leave nothing half-built.[vpc]on the EC2 demo, the OSI ingest endpoint, and the OpenSearch domain) and adds a network-topology header with the VPC/subnet/SG IDs. Prometheus, the Connected Data Source, and the UI stay untagged since they're regional services reached over the AWS-internal path. The public render is unchanged.Reachability model (why you can run this from anywhere)
A VPC-private domain's own endpoint isn't reachable from outside the VPC, but the managed OpenSearch UI (Application) endpoint is public and proxies to the domain over the AWS-internal path. So for VPC domains the installer:
MasterUserARN, no internal user DB), so SigV4 is authorized on a fresh domain with no in-VPC bootstrap;authorize-vpc-endpoint-accessfor theapplication.opensearchservice.amazonaws.comservice so the UI can reach the domain through its VPC endpoint (without this the UI returnsNo Living connections); andNo bastion or VPN is required.
Reliability: creation ordering / race conditions
A fresh deploy is now deterministic. Fixed four ways it could intermittently fail or silently not flow data:
Processingclearing, not just the endpoint URL appearing. The URL is published while the cluster is still initializing, which raced the security-API calls that run immediately after.No Living connectionserrors, and fails hard if it never lands. Previously a silent miss left the pipeline ACTIVE but unable to write.CreatePipelineandAddDirectQueryDataSourceretry on IAM role-not-propagated errors instead of relying on a fixed sleep.Architecture
General VPC deployment (the example shows two private subnets across AZs; one or three also work):
flowchart TB caller["Operator / CI<br/>runs the CLI; its IAM principal<br/>is the VPC domain master"] subgraph aws["AWS Account / Region"] direction TB subgraph clientrow[" "] direction LR user["Browser (dashboards)"] subgraph regional["Managed services (regional, outside the VPC)"] direction LR app["OpenSearch UI (Application)<br/>public endpoint"] cds["Connected Data Source<br/>OpenSearch to APS direct query"] amp["Amazon Managed Prometheus<br/>workspace (remote_write)"] end end subgraph vpc["VPC 10.0.0.0/16"] direction TB sg["Security group<br/>self-referencing intra-VPC allow;<br/>egress to NAT"] subgraph pub["Public subnet"] nat["NAT gateway</br>(when otel-demo enabled)"] end subgraph azA["Private subnet - AZ a"] domA["OpenSearch node + ENI<br/>(zone-aware)"] osiA["OSIS pipeline + ENI"] apps["Existing apps / pods<br/>(EKS / ECS workloads)<br/>emit OTLP :4317/:4318"] end subgraph azB["Private subnet - AZ b"] domB["OpenSearch node + ENI<br/>(zone-aware)"] osiB["OSIS pipeline + ENI"] ec2["EC2 demo instance (optional)<br/>OTel Collector + OTel Demo<br/>(OTLP :4317/:4318)"] end end end %% Telemetry data path: one OSIS pipeline, ENIs in both subnets; %% metrics egress drawn once from the ENI nearest APS to avoid a full-width edge apps e0@-->|"OTLP over SigV4 (osis:Ingest)"| osiA ec2 e1@-->|"OTLP over SigV4 (osis:Ingest)"| osiB osiA e2@-->|"logs / traces / service-map<br/>es:ESHttp*"| domA osiB e3@--> domB osiA e4@-->|"metrics</br>(to AMP via remote_write)"| amp %% Query / UI path user e5@-->|"HTTPS (AWS sign-in)"| app app e6@-->|"PPL query: logs / traces<br/>on the OpenSearch nodes"| domA app e7@-->|"metrics query via<br/>connected data source"| cds cds e8@-->|"PromQL direct query"| amp %% Control / setup path caller e9@-->|"create + FGAC role mapping<br/>via the UI (SigV4)"| app caller e10@-->|"authorize-vpc-endpoint-access<br/>for the UI service"| domA ec2 e11@-->|"image + repo pulls"| nat %% Animate the live telemetry data path (fast) and the query/UI path (slow) e0@{ animation: fast } e1@{ animation: fast } e2@{ animation: fast } e3@{ animation: fast } e4@{ animation: fast } e5@{ animation: slow } e6@{ animation: slow } e7@{ animation: slow } e8@{ animation: slow } %% Edge colors carry meaning: green telemetry, blue query/UI, muted control/setup linkStyle 0,1,2,3,4 stroke:#a3e635,stroke-width:2px linkStyle 5,6,7,8 stroke:#60a5fa,stroke-width:2px linkStyle 9,10,11 stroke:#a3a3a3,stroke-width:1.5px,stroke-dasharray:4 4 classDef ext fill:#eef,stroke:#557,color:#113; classDef net fill:#efe,stroke:#575,color:#131; classDef byoc fill:#fff,stroke:#888,stroke-width:1.5px,stroke-dasharray:5 4,color:#333; class app,amp,cds ext; class sg,nat net; class apps byoc; style clientrow fill:none,stroke:none;End-to-end verification
The change was deployed and verified end to end on a real VPC (2 private subnets across AZs, self-referencing security group, NAT egress). Domain, pipeline, and demo were all VPC-private; verification was driven entirely from outside the VPC.
Deployed
all_access,security_manager), the workspace, index patterns, and dashboards were all set up through it against the VPC-private domain.Data flowing (CloudWatch
AWS/OSIS, ~10 min window):otlp.traces.successRequests.countotel-logs-pipeline.opensearch.recordsIn.countotel-traces-pipeline.recordsProcessed.countotel-metrics-pipeline.prometheus.sinkMetricsSucceeded.countservice-map-pipeline.opensearch.recordsIn.countQueryable in the domain via the OpenSearch UI (SigV4, through the public Application endpoint that proxies to the VPC-private domain):
_countlogs-otel-v1*otel-v1-apm-span*otel-v2-apm-service-map*All test resources were torn down afterward with
destroy.Testing
node --test test/unit.test.mjs: 64 tests pass. New coverage: config-level VPC option validation, live VPC topology validation (each EC2 Describe branch, via injected accessors), the retry helper, the role-not-propagated and transient-HTTP classifiers, the backend-required guard, FGAC principal selection, and the architecture diagram's VPC annotations (including a regression check that box widths match the public render). Existing pipeline/EC2 coverage unchanged.