Metadata-Version: 2.1
Name: robhan_cdk_lib.aws_aps
Version: 0.1.4
Summary: AWS CDK Construct Library for Amazon Managed Service for Prometheus
Home-page: https://github.com/robert-hanuschke/cdk-aws_aps
Author: Robert Hanuschke<robhan-cdk-lib@hanuschke.eu>
License: MIT
Project-URL: Source, https://github.com/robert-hanuschke/cdk-aws_aps
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: JavaScript
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Typing :: Typed
Classifier: Development Status :: 5 - Production/Stable
Classifier: License :: OSI Approved
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: aws-cdk-lib <3.0.0,>=2.273.0
Requires-Dist: constructs <11.0.0,>=10.5.1
Requires-Dist: jsii <2.0.0,>=1.141.0
Requires-Dist: publication >=0.0.3

# @robhan-cdk-lib/aws_aps

Higher-level AWS Cloud Development Kit (CDK) constructs for Amazon Managed Service for Prometheus.

The upstream [`aws-cdk-lib/aws-aps`](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_aps-readme.html) module currently contains generated L1 constructs only. This library explores an L2 API intended to feel native to the AWS Construct Library:

* references to workspaces, EKS clusters, IAM roles, KMS keys, log groups, subnets, and security groups use CDK resource-reference interfaces rather than raw identifiers;
* owned and existing resources expose minimal, stable identity interfaces and token-safe `from...Arn` factories;
* scraper destinations, sources, and configuration encodings use factories that prevent invalid combinations;
* resource policies accept and validate `iam.PolicyDocument`;
* taggable resources support both `props.tags` and `Tags.of(...)`;
* all currently available `AWS::APS` CloudFormation resource types and properties are covered.

Schema constraints already expressed by CloudFormation, such as most string lengths and numeric ranges, are left to CloudFormation. Repeating them eagerly in an L2 is unsafe for unresolved CDK tokens and can drift from the service schema. A small set of concrete numeric invariants absent from that schema is checked locally; unresolved values and semantic content such as PromQL syntax remain service-validated.

This package is not supported or endorsed by AWS. Please report issues in the [project repository](https://github.com/robert-hanuschke/cdk-aws_aps/issues).

## Workspace and rule groups

A workspace has no required properties. Related constructs accept the generated `IWorkspaceRef` contract, so both this L2 and the upstream `CfnWorkspace` can be used.

```python
from robhan_cdk_lib.aws_aps import WorkspaceConfiguration
from aws_cdk import Stack, StackProps
from constructs import Construct
from robhan_cdk_lib.aws_aps import RuleGroupsNamespace, Workspace

class MetricsStack(Stack):
    def __init__(self, scope, id, *, description=None, env=None, stackName=None, tags=None, notificationArns=None, synthesizer=None, terminationProtection=None, analyticsReporting=None, crossRegionReferences=None, permissionsBoundary=None, suppressTemplateIndentation=None, propertyInjectors=None):
        super().__init__(scope, id, description=description, env=env, stackName=stackName, tags=tags, notificationArns=notificationArns, synthesizer=synthesizer, terminationProtection=terminationProtection, analyticsReporting=analyticsReporting, crossRegionReferences=crossRegionReferences, permissionsBoundary=permissionsBoundary, suppressTemplateIndentation=suppressTemplateIndentation, propertyInjectors=propertyInjectors)

        workspace = Workspace(self, "Workspace",
            alias="application-metrics",
            workspace_configuration=WorkspaceConfiguration(
                out_of_order_time_window_in_seconds=300,
                retention_period_in_days=365,
                rule_query_offset_in_seconds=60
            ),
            tags={"environment": "production"}
        )

        RuleGroupsNamespace(self, "Rules",
            workspace=workspace,
            name="availability",
            data="""groups:
              - name: availability
                rules:
                  - record: service:up:sum
                    expr: sum(up)
            """
        )
```

## Managed scraper

Scraper factories model the mutually exclusive destination and source variants. `ScrapeConfiguration.fromString()` accepts raw Prometheus YAML. The CloudFormation resource provider performs the service API blob serialization; do not base64-encode the value.

```python
from aws_cdk import Stack, StackProps
from aws_cdk.aws_ec2 import Subnet
from aws_cdk.aws_eks import Cluster
from constructs import Construct
from robhan_cdk_lib.aws_aps import ScrapeConfiguration, Scraper, ScraperDestination, ScraperSource, Workspace

class ScraperStack(Stack):
    def __init__(self, scope, id, *, description=None, env=None, stackName=None, tags=None, notificationArns=None, synthesizer=None, terminationProtection=None, analyticsReporting=None, crossRegionReferences=None, permissionsBoundary=None, suppressTemplateIndentation=None, propertyInjectors=None):
        super().__init__(scope, id, description=description, env=env, stackName=stackName, tags=tags, notificationArns=notificationArns, synthesizer=synthesizer, terminationProtection=terminationProtection, analyticsReporting=analyticsReporting, crossRegionReferences=crossRegionReferences, permissionsBoundary=permissionsBoundary, suppressTemplateIndentation=suppressTemplateIndentation, propertyInjectors=propertyInjectors)

        workspace = Workspace(self, "Workspace")
        cluster = Cluster.from_cluster_attributes(self, "Cluster",
            cluster_name="metrics-cluster"
        )
        subnet = Subnet.from_subnet_id(self, "Subnet", "subnet-0123456789abcdef0")

        Scraper(self, "Scraper",
            destination=ScraperDestination.amp(workspace),
            source=ScraperSource.eks(cluster=cluster, subnets=[subnet]),
            scrape_configuration=ScrapeConfiguration.from_string("""scrape_configs:
                  - job_name: kubernetes-pods
                    kubernetes_sd_configs:
                      - role: pod
                """)
        )
```

`ScraperDestination.cloudWatch()` supports the account's CloudWatch dataset. `ScraperSource.vpc()` supports VPC-reachable Prometheus sources and must be paired with a CloudWatch destination; EKS sources can use either AMP or CloudWatch.

### Scraper prerequisites

A scraper does not modify the supplied network, EKS authorization, IAM roles, or KMS policy:

* An EKS source requires private cluster endpoint access, VPC DNS, and available addresses in every selected subnet. Automatic access entries require EKS authentication mode `API` or `API_AND_CONFIG_MAP`; otherwise configure the required Kubernetes RBAC manually. See [Set up managed collectors for Amazon EKS](https://docs.aws.amazon.com/prometheus/latest/userguide/AMP-collector-how-to.html).
* For cross-account collection, provide both source and target roles and configure their trust and permissions. The same explicit role configuration is required when the destination workspace uses a customer-managed KMS key, including a same-account scraper.
* For VPC sources, prefer private subnets and security-group egress limited to the intended Prometheus endpoints. The construct does not add broad network rules.

## Anomaly detection

`MissingDataAction` and `IgnoreNearExpected` represent service API unions, so callers cannot accidentally request incompatible alternatives. Random Cut Forest queries must be wrapped by `avg`, `count`, `group`, `max`, `min`, `quantile`, `stddev`, `stdvar`, or `sum`; Amazon Managed Service for Prometheus validates the PromQL semantics.

```python
from robhan_cdk_lib.aws_aps import AnomalyDetectorConfiguration, RandomCutForestConfiguration
from aws_cdk import Stack, StackProps
from constructs import Construct
from robhan_cdk_lib.aws_aps import AnomalyDetector, IgnoreNearExpected, MissingDataAction, Workspace

class AnomalyStack(Stack):
    def __init__(self, scope, id, *, description=None, env=None, stackName=None, tags=None, notificationArns=None, synthesizer=None, terminationProtection=None, analyticsReporting=None, crossRegionReferences=None, permissionsBoundary=None, suppressTemplateIndentation=None, propertyInjectors=None):
        super().__init__(scope, id, description=description, env=env, stackName=stackName, tags=tags, notificationArns=notificationArns, synthesizer=synthesizer, terminationProtection=terminationProtection, analyticsReporting=analyticsReporting, crossRegionReferences=crossRegionReferences, permissionsBoundary=permissionsBoundary, suppressTemplateIndentation=suppressTemplateIndentation, propertyInjectors=propertyInjectors)

        workspace = Workspace.from_workspace_arn(self, "Workspace", "arn:aws:aps:us-east-1:123456789012:workspace/ws-example")

        AnomalyDetector(self, "Detector",
            workspace=workspace,
            alias="request-rate",
            configuration=AnomalyDetectorConfiguration(
                random_cut_forest=RandomCutForestConfiguration(
                    query="sum(rate(requests_total[5m]))",
                    ignore_near_expected_from_above=IgnoreNearExpected.ratio(0.1)
                )
            ),
            missing_data_action=MissingDataAction.SKIP
        )
```

## Resource policies

Resource policies use the IAM policy model, validate resource-policy requirements at synthesis, and render lazily so statements added after construction are included. If the workspace uses a customer-managed KMS key, every cross-account principal also needs access to that key; the resource policy alone is insufficient.

```python
from aws_cdk import Stack, StackProps
from aws_cdk.aws_iam import AccountPrincipal, PolicyDocument, PolicyStatement
from constructs import Construct
from robhan_cdk_lib.aws_aps import ResourcePolicy, Workspace

class PolicyStack(Stack):
    def __init__(self, scope, id, *, description=None, env=None, stackName=None, tags=None, notificationArns=None, synthesizer=None, terminationProtection=None, analyticsReporting=None, crossRegionReferences=None, permissionsBoundary=None, suppressTemplateIndentation=None, propertyInjectors=None):
        super().__init__(scope, id, description=description, env=env, stackName=stackName, tags=tags, notificationArns=notificationArns, synthesizer=synthesizer, terminationProtection=terminationProtection, analyticsReporting=analyticsReporting, crossRegionReferences=crossRegionReferences, permissionsBoundary=permissionsBoundary, suppressTemplateIndentation=suppressTemplateIndentation, propertyInjectors=propertyInjectors)

        workspace = Workspace.from_workspace_arn(self, "Workspace", "arn:aws:aps:us-east-1:123456789012:workspace/ws-example")

        ResourcePolicy(self, "ResourcePolicy",
            workspace=workspace,
            policy_document=PolicyDocument(
                statements=[PolicyStatement(
                    actions=["aps:QueryMetrics"],
                    principals=[AccountPrincipal("210987654321")],
                    resources=[workspace.workspace_arn]
                )]
            )
        )
```

## Referencing existing resources

Each resource with an ARN has a token-safe reference factory:

* `Workspace.fromWorkspaceArn()` and `Workspace.fromWorkspaceAttributes()`
* `RuleGroupsNamespace.fromRuleGroupsNamespaceArn()`
* `Scraper.fromScraperArn()`
* `AnomalyDetector.fromAnomalyDetectorArn()`
* `ResourcePolicy.fromWorkspaceArn()`

## Validation commands

* `yarn build` compiles the jsii API, strictly compiles README examples, runs unit tests and the isolated test-project type check, lints, and packages JavaScript, Java, Python, .NET, and Go.
* `yarn integ:snapshot` verifies or updates the integration snapshot without contacting AWS.
* `yarn integ` deploys the integration test in `us-east-1`; run it as `AWS_PROFILE=<non-production-profile> AWS_REGION=us-east-1 yarn integ` and verify cleanup.
* `yarn compat` compares the jsii surface against the latest published package.

## Compatibility

Version `0.1.0` introduces this core-aligned API and intentionally breaks the earlier `0.0.x` prototype API. The exact reviewed breaks are enumerated in `.compatignore`; additional jsii incompatibilities still fail the compatibility gate.

## Upstream status

The implementation follows the current [AWS CDK contributing guide](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md), [construct design guidelines](https://github.com/aws/aws-cdk/blob/main/docs/DESIGN_GUIDELINES.md), and the adjacent Grafana construct library as local prior art. A core contribution still requires design consensus, an upstream issue or RFC, a port into the `aws-cdk` monorepo, and a repeat of deploy-backed integration verification in the upstream test layout.

## License

MIT
