From 55bc4bcad16512bc4b92212aa4b1fa564af95f72 Mon Sep 17 00:00:00 2001 From: Jamie Mullan <46033424+jamiepmullan@users.noreply.github.com> Date: Thu, 19 Mar 2026 16:28:14 +0000 Subject: [PATCH 1/5] mediaconnect design --- text/0884-aws-elemental-mediaconnect-l2.md | 1193 ++++++++++++++++++++ 1 file changed, 1193 insertions(+) create mode 100644 text/0884-aws-elemental-mediaconnect-l2.md diff --git a/text/0884-aws-elemental-mediaconnect-l2.md b/text/0884-aws-elemental-mediaconnect-l2.md new file mode 100644 index 000000000..e9f87ff97 --- /dev/null +++ b/text/0884-aws-elemental-mediaconnect-l2.md @@ -0,0 +1,1193 @@ +# RFC AWS Elemental MediaConnect CDK L2 Construct + +## L2 Constructs for AWS Elemental MediaConnect Flow, Bridge, Gateway and Router resources + +* **Original Author(s):** @jamiepmullan +* **Tracking Issue:** [#884](https://github.com/aws/aws-cdk-rfcs/issues/884) +* **API Bar Raiser:** TBD + +This design outlines how we build an L2 construct for AWS Elemental MediaConnect, delivering the following benefits: + +- Implement sensible defaults to make getting started with AWS Elemental MediaConnect easier +- Simplify the declaration of Flow, Bridge, and Gateway by using factory patterns instead of complex union types +- Make VPC integration easier with reusable VpcInterface objects +- Abstract source configurations using enum-like classes for RTP, SRT, RIST, and entitlement sources +- Provide faster validation within CDK to ensure parameters are compatible with each other +- Include helper functions such as: + - `.addOutput()` to make output definitions easier for both flows and bridges + - Add metric functions to provide default/basic CloudWatch metrics as per CDK design +- Use types instead of string literals to simplify definitions (Duration, enums) + +The code sample below is a simple configuration comparison between the existing L1 construct and what a L2 _could_ look like: + +```ts +const flow = new CfnFlow(stack, 'Flow', { + name: 'my-flow', + source: { + name: 'my-source', + protocol: 'rtp', + ingestPort: 5000, + whitelistCidr: '203.0.113.0/24', + description: 'RTP source', + }, + vpcInterfaces: [{ + name: 'my-vpc-interface', + roleArn: role.roleArn, + securityGroupIds: [securityGroup.securityGroupId], + subnetId: subnet.subnetId, + networkInterfaceType: 'ena', + }], +}); + +const bridge = new CfnBridge(stack, 'Bridge', { + name: 'my-bridge', + placementArn: gateway.gatewayArn, + egressGatewayBridge: { + maxBitrate: 10000000, + }, + sources: [{ + flowSource: { + name: 'flow-source', + flowArn: flow.attrFlowArn, + flowVpcInterfaceAttachment: { + vpcInterfaceName: 'my-vpc-interface', + }, + }, + }], + outputs: [{ + networkOutput: { + name: 'network-output', + ipAddress: '192.168.1.100', + port: 5000, + protocol: 'rtp', + networkName: 'my-network', + ttl: 64, + }, + }], +}); +``` + +Same Configuration in an example L2: + +```ts +const vpcInterface = mediaconnect.VpcInterface.create({ + vpcInterfaceName: 'my-vpc-interface', + role: role, + securityGroups: [securityGroup], + subnet: subnet, +}); + +const flow = new mediaconnect.Flow(stack, 'Flow', { + flowName: 'my-flow', + source: mediaconnect.SourceConfiguration.rtp({ + flowSourceName: 'my-source', + port: 5000, + network: mediaconnect.NetworkConfiguration.internet('203.0.113.0/24'), + }), + vpcInterfaces: [vpcInterface], +}); + +const bridge = new mediaconnect.Bridge(stack, 'Bridge', { + bridgeName: 'my-bridge', + gateway: gateway, + config: mediaconnect.BridgeConfiguration.egress({ + maxBitrate: mediaconnect.Bitrate.mbps(10), + flowSources: [{ + name: 'flow-source', + flow: flow, + vpcInterface: vpcInterface, + }], + networkOutputs: [ + mediaconnect.BridgeOutputConfiguration.network({ + name: 'network-output', + ipAddress: '192.168.1.100', + port: 5000, + protocol: mediaconnect.BridgeProtocol.RTP, + networkName: 'my-network', + ttl: 64, + }), + ], + }), +}); +``` + +The rest of this doc outlines the design for an L2 construct. + +## Working Backwards + +### README + +[AWS Elemental MediaConnect](https://aws.amazon.com/mediaconnect/) is a high-quality transport service for live video that provides the reliability and security of satellite and fiber-optic combined with the flexibility, agility, and economics of IP-based networks. MediaConnect enables you to build mission-critical live video workflows in a fraction of the time and cost of satellite or fiber services. + +Without an L2 construct, developers define MediaConnect flows and bridges via the AWS console, the AWS CLI, and Infrastructure as Code tools like CloudFormation and CDK. + +However, there are several challenges to defining MediaConnect resources at scale that an L2 construct can resolve. For example, developers must reference documentation to determine the valid combinations of parameters for different source types and bridge configurations. + +We could greatly simplify the developer experience in CDK by introducing MediaConnect L2 constructs. This will have a mixture of sensible defaults as well as methods to help build correct configurations of Flows, Bridges, Gateways, Routers, and VPC interfaces. + +* [What is AWS Elemental MediaConnect?](https://aws.amazon.com/mediaconnect/) +* [MediaConnect Documentation](https://docs.aws.amazon.com/mediaconnect/latest/ug/what-is.html) +* [MediaConnect L1 (CloudFormation) Constructs](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/AWS_MediaConnect.html) + +#### AWS Elemental MediaConnect Flow + +A MediaConnect flow represents a transport stream connection between a source and one or more outputs. Flows are the primary resource for transporting live video content. + +For further information refer to [our documentation](https://docs.aws.amazon.com/mediaconnect/latest/ug/flows.html). + +```ts +new mediaconnect.Flow(stack, 'MyFlow', { + source: mediaconnect.SourceConfiguration.rtp({ + flowSourceName: 'my-source', + port: 5000, + network: mediaconnect.NetworkConfiguration.internet('203.0.113.0/24'), + }), +}); +``` + +Optional override examples: + +```ts +new mediaconnect.Flow(stack, 'MyFlow', { + flowName: 'my-live-stream', + source: mediaconnect.SourceConfiguration.rtp({ + flowSourceName: 'my-source', + port: 5000, + network: mediaconnect.NetworkConfiguration.internet('203.0.113.0/24'), + }), +}); +``` + +##### Adding Outputs to Flows + +The `addOutput()` method provides a convenient way to create outputs directly associated with a flow. Outputs define where and how the flow sends content to downstream destinations. + +```ts +const flow = new mediaconnect.Flow(stack, 'MyFlow', { + source: mediaconnect.SourceConfiguration.rtp({ + flowSourceName: 'my-source', + port: 5000, + network: mediaconnect.NetworkConfiguration.internet('203.0.113.0/24'), + }), +}); + +// Add an RTP output +const rtpOutput = flow.addOutput('RtpOutput', + mediaconnect.OutputConfiguration.rtp({ + destination: '198.51.100.10', + port: 5001, + }), +); + +// Add an SRT output with encryption +const srtOutput = flow.addOutput('SrtOutput', + mediaconnect.OutputConfiguration.srtCaller({ + destination: '198.51.100.11', + port: 5002, + minLatency: Duration.millis(200), + }), +); + +// Add a router output +const routerOutput = flow.addOutput('RouterOutput', + mediaconnect.OutputConfiguration.router(), +); +``` + +Interface for Flow Output: + +```ts +/** + * Interface for MediaConnect Flow Output + */ +export interface IFlowOutput extends IResource { + /** + * The Amazon Resource Name (ARN) of the flow output + * + * @attribute + */ + readonly flowOutputArn: string; +} +``` + +Flow outputs support various protocols and configurations: + +- **RTP/RTP-FEC**: Real-time Transport Protocol for standard streaming +- **SRT**: Secure Reliable Transport with configurable latency +- **RIST**: Reliable Internet Stream Transport +- **Zixi**: Zixi protocol for reliable transport +- **CDI**: Cloud Digital Interface for uncompressed workflows + +Outputs can include encryption, VPC interface attachments, and router integration for advanced workflows. + +Property Interface for Flow: + +```ts +FlowProps { + /** + * Flow Name + */ + flowName?: string; + + /** + * Source configuration for the flow + */ + source: SourceConfiguration; + + /** + * Determines the processing capacity and feature set of the flow. + * MEDIUM for standard transport streams, LARGE for NDI, LARGE_4X for CDI/JPEG XS. + * @default - MEDIUM + */ + flowSize?: FlowSize; + + /** + * VPC interfaces for the flow + */ + vpcInterfaces?: VpcInterfaceConfig[]; + + /** + * Source failover configuration + */ + sourceFailoverConfig?: FailoverConfig; + + /** + * Source monitoring configuration + */ + sourceMonitoringConfig?: SourceMonitoringConfig; + + /** + * NDI configuration (requires LARGE flow size) + */ + ndiConfig?: NdiConfig; + + /** + * Maintenance window configuration + */ + maintenance?: MaintenanceWindow; + + /** + * Media streams for CDI and JPEG XS workflows + */ + mediaStream?: MediaStream[]; + + /** + * Policy to apply when the Flow is removed from the stack + * + * @default RemovalPolicy.RETAIN + */ + removalPolicy?: RemovalPolicy; +} +``` + +Interface example of what the Flow will implement: + +```ts +/** + * Interface for MediaConnect Flow + */ +export interface IFlow extends IResource, IFlowRef { + /** + * The Amazon Resource Name (ARN) of the flow + * + * @attribute + */ + readonly flowArn: string; + + /** + * The Amazon Resource Name (ARN) of the primary source + * + * @attribute + */ + readonly sourceArn: string; + + /** + * Whether failover is enabled + */ + readonly isFailoverEnabled?: boolean; + + /** + * Add an output to this flow + */ + addOutput(id: string, outputConfig: OutputConfiguration): IFlowOutput; + + /** + * Create a CloudWatch metric + */ + metric(metricName: string, props?: MetricOptions): Metric; + + /** + * Metric for source bitrate + * @default - average over 60 seconds + */ + metricSourceBitrate(props?: MetricOptions): Metric; + + /** + * Metric for packets not recovered + * @default - sum over 60 seconds + */ + metricSourceNotRecoveredPackets(props?: MetricOptions): Metric; + + /** + * Metric for total packets received + * @default - sum over 60 seconds + */ + metricSourceTotalPackets(props?: MetricOptions): Metric; + + /** + * Metric for failover source selection + * @default - maximum over 60 seconds + */ + metricFailoverSourceSelected(props?: MetricOptions): Metric; +} +``` + +Provide fromMethod capability - allowing imports of the resource: + +```ts +/** + * Creates a Flow construct that represents an external (imported) Flow + */ +public static fromFlowAttributes(scope: Construct, id: string, attrs: FlowAttributes): IFlow { + class Import extends FlowBase implements IFlow { + public readonly flowArn = attrs.flowArn; + public readonly sourceArn = attrs.primarySourceArn; + public readonly isFailoverEnabled = attrs.isFailoverEnabled ?? false; + } + + return new Import(scope, id); +} +``` + +##### Flow Source Types + +MediaConnect supports multiple source types for ingesting content into a flow: + +###### SRT Listener Source + +```ts +new mediaconnect.Flow(stack, 'MyFlow', { + source: mediaconnect.SourceConfiguration.srtListener({ + flowSourceName: 'live-encoder-source', + description: 'Live encoder feed', + port: 5000, + minLatency: Duration.millis(2000), + network: mediaconnect.NetworkConfiguration.internet('203.0.113.0/24'), + }), +}); +``` + +###### VPC Source + +```ts +declare const securityGroup: ec2.ISecurityGroup; +declare const subnet: ec2.ISubnet; +declare const role: iam.IRole; + +const vpcInterface = mediaconnect.VpcInterface.create({ + vpcInterfaceName: 'my-vpc-interface', + role: role, + securityGroups: [securityGroup], + subnet: subnet, +}); + +new mediaconnect.Flow(stack, 'MyFlow', { + source: mediaconnect.SourceConfiguration.rist({ + flowSourceName: 'vpc-source', + description: 'VPC-based source', + port: 5000, + maxLatency: Duration.millis(2000), + network: mediaconnect.NetworkConfiguration.vpc(vpcInterface), + }), + vpcInterfaces: [vpcInterface], +}); +``` + +###### Entitled Source (From Another AWS Account) + +```ts +// Import an entitlement from another AWS account +const entitlement = mediaconnect.FlowEntitlement.fromFlowEntitlementAttributes(stack, 'ImportedEntitlement', { + entitlementArn: 'arn:aws:mediaconnect:us-west-2:111122223333:entitlement:1-11111111111111111111111111111111:MyEntitlement', +}); + +new mediaconnect.Flow(stack, 'MyFlow', { + source: mediaconnect.SourceConfiguration.entitlement({ + entitlement: entitlement, + }), +}); +``` + +#### AWS Elemental MediaConnect VPC Interface + +VPC interfaces enable MediaConnect flows to send and receive content through your Amazon VPC, providing secure, private connectivity between MediaConnect and your VPC resources. VPC interfaces are essential for integrating MediaConnect with other AWS services within your VPC, such as connecting flows to bridges or routing content through private networks. + +For further information refer to [our documentation](https://docs.aws.amazon.com/mediaconnect/latest/ug/vpc-interfaces.html). + +##### Creating VPC Interfaces + +VPC interfaces are created using the `VpcInterface.create()` factory method and can be reused across multiple flows and bridges: + +```ts +declare const role: iam.IRole; +declare const securityGroup: ec2.ISecurityGroup; +declare const subnet: ec2.ISubnet; + +const vpcInterface = mediaconnect.VpcInterface.create({ + vpcInterfaceName: 'my-vpc-interface', + role: role, + securityGroups: [securityGroup], + subnet: subnet, +}); +``` + +##### Network Interface Types + +You can specify the network interface type for performance optimization: + +```ts +declare const role: iam.IRole; +declare const securityGroup: ec2.ISecurityGroup; +declare const subnet: ec2.ISubnet; + +// ENA (Elastic Network Adapter) - Standard performance +const enaInterface = mediaconnect.VpcInterface.create({ + vpcInterfaceName: 'ena-interface', + role: role, + securityGroups: [securityGroup], + subnet: subnet, + networkInterfaceType: mediaconnect.NetworkInterface.ENA, +}); + +// EFA (Elastic Fabric Adapter) - Required for CDI workflows +const efaInterface = mediaconnect.VpcInterface.create({ + vpcInterfaceName: 'efa-interface', + role: role, + securityGroups: [securityGroup], + subnet: subnet, + networkInterfaceType: mediaconnect.NetworkInterface.EFA, +}); +``` + +##### Using Existing Network Interfaces + +If you have pre-created network interfaces, you can reference them: + +```ts +declare const role: iam.IRole; +declare const securityGroup: ec2.ISecurityGroup; +declare const subnet: ec2.ISubnet; + +const vpcInterface = mediaconnect.VpcInterface.create({ + vpcInterfaceName: 'existing-interface', + role: role, + securityGroups: [securityGroup], + subnet: subnet, + networkInterfaceIds: ['eni-1234567890abcdef0', 'eni-0987654321fedcba0'], +}); +``` + +**Note:** You cannot specify both `networkInterfaceType` and `networkInterfaceIds`. Use `networkInterfaceType` to let MediaConnect create interfaces automatically, or `networkInterfaceIds` to use existing interfaces. + +##### Using VPC Interfaces with Flows + +VPC interfaces enable flows to use VPC-based sources and outputs: + +```ts +declare const role: iam.IRole; +declare const securityGroup: ec2.ISecurityGroup; +declare const subnet: ec2.ISubnet; + +const vpcInterface = mediaconnect.VpcInterface.create({ + vpcInterfaceName: 'flow-vpc-interface', + role: role, + securityGroups: [securityGroup], + subnet: subnet, +}); + +const flow = new mediaconnect.Flow(stack, 'VpcFlow', { + source: mediaconnect.SourceConfiguration.rist({ + flowSourceName: 'vpc-source', + port: 5000, + maxLatency: Duration.millis(2000), + network: mediaconnect.NetworkConfiguration.vpc(vpcInterface), + }), + vpcInterfaces: [vpcInterface], +}); +``` + +##### Using VPC Interfaces with Bridges + +VPC interfaces are required for egress bridges to connect cloud-based flows to on-premises equipment: + +```ts +declare const gateway: mediaconnect.IGateway; +declare const flow: mediaconnect.IFlow; +declare const vpcInterface: mediaconnect.VpcInterfaceConfig; + +new mediaconnect.Bridge(stack, 'EgressBridge', { + bridgeName: 'my-egress-bridge', + gateway: gateway, + config: mediaconnect.BridgeConfiguration.egress({ + maxBitrate: mediaconnect.Bitrate.mbps(10), + flowSources: [{ + name: 'cloud-source', + flow: flow, + vpcInterface: vpcInterface, + }], + networkOutputs: [/* ... */], + }), +}); +``` + +Property Interface for VpcInterface: + +```ts +VpcInterfaceProps { + /** + * Unique name for the VPC interface + */ + name: string; + + /** + * IAM role that MediaConnect assumes to create ENIs in your account + */ + role: IRole; + + /** + * Security groups to apply to the ENI + */ + securityGroups: ISecurityGroup[]; + + /** + * Subnet where the ENI will be created (must be in the same AZ as the flow) + */ + subnet: ISubnet; + + /** + * Pre-created network interface IDs + * @default - MediaConnect creates network interfaces automatically + */ + networkInterfaceIds?: string[]; + + /** + * Network interface type (ENA or EFA) + * @default - Default network interface type + */ + networkInterfaceType?: NetworkInterface; +} +``` + +Interface example of what VpcInterface implements: + +```ts +/** + * VPC Interface configuration + */ +export interface VpcInterfaceConfig { + /** + * Unique name for the VPC interface + */ + readonly name: string; + + /** + * IAM role for ENI creation + */ + readonly role: IRole; + + /** + * Security groups for the ENI + */ + readonly securityGroups: ISecurityGroup[]; + + /** + * Subnet for the ENI + */ + readonly subnet: ISubnet; + + /** + * Network interface IDs (if using existing interfaces) + */ + readonly networkInterfaceIds?: string[]; + + /** + * Network interface type + */ + readonly networkInterfaceType?: NetworkInterface; +} +``` + +#### AWS Elemental MediaConnect Gateway + +MediaConnect gateways enable hybrid cloud workflows by allowing on-premises equipment to connect to AWS cloud resources. Gateways define the network infrastructure that bridges use to transport video between on-premises and cloud environments. + +For further information refer to [our documentation](https://docs.aws.amazon.com/mediaconnect/latest/ug/gateways.html). + +```ts +const gateway = new mediaconnect.Gateway(stack, 'MyGateway', { + gatewayName: 'my-gateway', + egressCidrBlocks: ['10.0.0.0/16'], + networks: [{ + cidrBlock: '192.168.1.0/24', + name: 'production-network', + }], +}); +``` + +##### Adding Networks to Gateways + +Networks can be added at creation time or dynamically using the `addNetwork()` method: + +```ts +const gateway = new mediaconnect.Gateway(stack, 'MyGateway', { + gatewayName: 'my-gateway', + egressCidrBlocks: ['10.0.0.0/16'], + networks: [{ + cidrBlock: '192.168.1.0/24', + name: 'production-network', + }], +}); + +// Add additional networks dynamically +gateway.addNetwork({ + cidrBlock: '192.168.2.0/24', + name: 'backup-network', +}); + +gateway.addNetwork({ + cidrBlock: '192.168.3.0/24', + name: 'test-network', +}); +``` + +##### Importing Existing Gateways + +You can import existing gateways for use in your CDK application: + +```ts +const gateway = mediaconnect.Gateway.fromAttributes(stack, 'ImportedGateway', { + gatewayArn: 'arn:aws:mediaconnect:us-west-2:111122223333:gateway:1-11111111111111111111111111111111:MyGateway', +}); + +// Use the imported gateway with bridges +new mediaconnect.Bridge(stack, 'MyBridge', { + bridgeName: 'my-bridge', + gateway: gateway, + config: mediaconnect.BridgeConfiguration.egress({ + maxBitrate: mediaconnect.Bitrate.mbps(10), + flowSources: [/* ... */], + networkOutputs: [/* ... */], + }), +}); +``` + +Property Interface for Gateway: + +```ts +GatewayProps { + /** + * Gateway Name + */ + gatewayName?: string; + + /** + * The range of IP addresses that are allowed to contribute content or initiate output requests + */ + egressCidrBlocks: string[]; + + /** + * The list of networks in the gateway + */ + networks?: IGatewayNetwork[]; +} +``` + +Interface example of what the Gateway will implement: + +```ts +/** + * Interface for MediaConnect Gateway + */ +export interface IGateway extends IResource, IGatewayRef { + /** + * The Amazon Resource Name (ARN) of the gateway + * + * @attribute + */ + readonly gatewayArn: string; + + /** + * Add a network to this gateway + */ + addNetwork(network: IGatewayNetwork): IGatewayNetwork; +} +``` + +#### AWS Elemental MediaConnect Bridge + +MediaConnect bridges enable you to interconnect on-premises equipment with cloud-based workflows. Bridges support both ingress (on-premises to cloud) and egress (cloud to on-premises) scenarios. Bridges must be associated with a gateway to function. + +For further information refer to [our documentation](https://docs.aws.amazon.com/mediaconnect/latest/ug/bridges.html). + +Property Interface for Bridge: + +```ts +BridgeProps { + /** + * Bridge Name + */ + bridgeName?: string; + + /** + * Bridge configuration (ingress or egress) + */ + config: BridgeConfiguration; + + /** + * Gateway associated with the bridge + */ + gateway: IGateway; + + /** + * Description of the Bridge + */ + description?: string; + + /** + * Tagging for your Bridge + */ + tags?: { [key: string]: string }; + + /** + * Policy to apply when the Bridge is removed from the stack + * + * @default RemovalPolicy.RETAIN + */ + removalPolicy?: RemovalPolicy; +} +``` + +###### Ingress Bridge (On-premises to Cloud) + +```ts +const gateway = new mediaconnect.Gateway(stack, 'MyGateway', { + gatewayName: 'my-gateway', + egressCidrBlocks: ['10.0.0.0/16'], + networks: [{ + cidrBlock: '192.168.1.0/24', + name: 'production-network', + }], +}); + +new mediaconnect.Bridge(stack, 'MyIngressBridge', { + bridgeName: 'my-ingress-bridge', + config: mediaconnect.BridgeConfiguration.ingress({ + maxBitrate: mediaconnect.Bitrate.mbps(10), + maxOutputs: 2, + networkSources: [{ + name: 'on-prem-source', + protocol: mediaconnect.BridgeProtocol.RTP, + networkName: 'production-network', + multicastIp: '224.1.1.1', + port: 5000, + }], + }), + gateway: gateway, +}); +``` + +###### Egress Bridge (Cloud to On-premises) + +```ts +declare const gateway: mediaconnect.IGateway; +declare const flow: mediaconnect.IFlow; +declare const vpcInterface: mediaconnect.VpcInterfaceConfig; + +new mediaconnect.Bridge(stack, 'MyEgressBridge', { + bridgeName: 'my-egress-bridge', + config: mediaconnect.BridgeConfiguration.egress({ + maxBitrate: mediaconnect.Bitrate.mbps(10), + flowSources: [{ + name: 'cloud-source', + flow: flow, + vpcInterface: vpcInterface, + }], + networkOutputs: [ + mediaconnect.BridgeOutputConfiguration.network({ + name: 'on-prem-output', + ipAddress: '192.168.1.200', + port: 5001, + networkName: 'production-network', + protocol: mediaconnect.BridgeProtocol.RTP, + ttl: 64, + }), + ], + }), + gateway: gateway, +}); +``` + +#### AWS Elemental MediaConnect Router + +MediaConnect routers provide high-performance, low-latency video routing capabilities for building complex live video workflows. Router resources include network interfaces, inputs, and outputs. + +For further information refer to [our documentation](https://docs.aws.amazon.com/mediaconnect/latest/ug/routers.html). + +##### Router Network Interfaces + +Network interfaces define the network connectivity for router inputs and outputs: + +```ts +// Public network interface +const publicInterface = new mediaconnect.RouterNetworkInterface(stack, 'PublicInterface', { + routerNetworkInterfaceName: 'public-interface', + configuration: mediaconnect.RouterNetworkConfiguration.internet({ + cidr: ['10.0.0.0/16'], + }), +}); + +// Private network interface +declare const securityGroup: ec2.ISecurityGroup; +declare const subnet: ec2.ISubnet; + +const privateInterface = new mediaconnect.RouterNetworkInterface(stack, 'PrivateInterface', { + routerNetworkInterfaceName: 'private-interface', + configuration: mediaconnect.RouterNetworkConfiguration.vpc({ + securityGroups: [securityGroup], + subnet: subnet, + }), +}); +``` + +##### Router Inputs + +Router inputs receive live video streams from various sources: + +```ts +// Standard input with RTP protocol +declare const networkInterface: mediaconnect.IRouterNetworkInterface; + +const input = new mediaconnect.RouterInput(stack, 'RtpInput', { + routerInputName: 'rtp-input', + maximumBitrate: mediaconnect.Bitrate.mbps(10), + routingScope: mediaconnect.RoutingScope.REGIONAL, + tier: mediaconnect.RouterInputTier.INPUT_100, + availabilityZone: 'us-east-1a', + configuration: mediaconnect.RouterInputConfiguration.standard({ + networkInterface: networkInterface, + protocol: mediaconnect.RouterInputProtocol.rtp({ + port: 5000, + }), + }), +}); + +// MediaConnect Flow input +declare const flow: mediaconnect.IFlow; +declare const flowOutput: mediaconnect.IFlowOutput; + +const flowInput = new mediaconnect.RouterInput(stack, 'FlowInput', { + routerInputName: 'flow-input', + maximumBitrate: mediaconnect.Bitrate.mbps(20), + routingScope: mediaconnect.RoutingScope.REGIONAL, + tier: mediaconnect.RouterInputTier.INPUT_50, + availabilityZone: 'us-east-1a', + configuration: mediaconnect.RouterInputConfiguration.mediaConnectFlow({ + flow: flow, + flowOutput: flowOutput, + }), +}); + +// MediaConnect Flow input without specific connection (requires explicit AZ) +const flowInputNoConnection = new mediaconnect.RouterInput(stack, 'FlowInputNoConnection', { + routerInputName: 'flow-input-no-connection', + maximumBitrate: mediaconnect.Bitrate.mbps(20), + routingScope: mediaconnect.RoutingScope.REGIONAL, + tier: mediaconnect.RouterInputTier.INPUT_50, + availabilityZone: 'us-east-1a', + configuration: mediaconnect.RouterInputConfiguration.mediaConnectFlowWithoutConnection({ + availabilityZone: 'us-east-1a', + }), +}); +``` + +##### Router Outputs + +Router outputs send video streams to various destinations: + +```ts +// Standard output with SRT protocol +declare const networkInterface: mediaconnect.IRouterNetworkInterface; + +const output = new mediaconnect.RouterOutput(stack, 'SrtOutput', { + routerOutputName: 'srt-output', + maximumBitrate: mediaconnect.Bitrate.mbps(10), + routingScope: mediaconnect.RoutingScope.REGIONAL, + tier: mediaconnect.RouterOutputTier.OUTPUT_100, + configuration: mediaconnect.RouterOutputConfiguration.standard({ + protocol: mediaconnect.RouterOutputProtocol.srtListener({ + port: 9001, + minimumLatency: Duration.millis(200), + }), + networkInterface: networkInterface, + }), +}); + +// MediaLive output +declare const mediaLiveInput: medialive.CfnInput; + +const mediaLiveOutput = new mediaconnect.RouterOutput(stack, 'MediaLiveOutput', { + routerOutputName: 'medialive-output', + maximumBitrate: mediaconnect.Bitrate.mbps(15), + routingScope: mediaconnect.RoutingScope.GLOBAL, + tier: mediaconnect.RouterOutputTier.OUTPUT_50, + configuration: mediaconnect.RouterOutputConfiguration.mediaLiveInput({ + mediaLiveInputArn: mediaLiveInput.attrArn, + mediaLivePipelineId: mediaconnect.MediaLivePipeline.PIPELINE_0, + }), +}); + +// MediaLive output without specific connection (requires explicit AZ) +const mediaLiveOutputNoConnection = new mediaconnect.RouterOutput(stack, 'MediaLiveOutputNoConnection', { + routerOutputName: 'medialive-output-no-connection', + maximumBitrate: mediaconnect.Bitrate.mbps(15), + routingScope: mediaconnect.RoutingScope.GLOBAL, + tier: mediaconnect.RouterOutputTier.OUTPUT_50, + configuration: mediaconnect.RouterOutputConfiguration.mediaLiveInputWithoutConnection({ + availabilityZone: 'us-east-1a', + }), +}); + +// MediaConnect Flow output +declare const flow: mediaconnect.IFlow; + +const flowOutput = new mediaconnect.RouterOutput(stack, 'FlowOutput', { + routerOutputName: 'flow-output', + maximumBitrate: mediaconnect.Bitrate.mbps(20), + routingScope: mediaconnect.RoutingScope.REGIONAL, + tier: mediaconnect.RouterOutputTier.OUTPUT_100, + configuration: mediaconnect.RouterOutputConfiguration.mediaConnectFlow({ + flow: flow, + }), +}); + +// MediaConnect Flow output without specific connection (requires explicit AZ) +const flowOutputNoConnection = new mediaconnect.RouterOutput(stack, 'FlowOutputNoConnection', { + routerOutputName: 'flow-output-no-connection', + maximumBitrate: mediaconnect.Bitrate.mbps(20), + routingScope: mediaconnect.RoutingScope.REGIONAL, + tier: mediaconnect.RouterOutputTier.OUTPUT_100, + configuration: mediaconnect.RouterOutputConfiguration.mediaConnectFlowWithoutConnection({ + availabilityZone: 'us-east-1a', + }), +}); +``` + +##### Adding Outputs to Bridges + +For egress bridges, the `addOutput()` method provides a convenient way to add network outputs: + +```ts +const bridge = new mediaconnect.Bridge(stack, 'MyEgressBridge', { + bridgeName: 'my-egress-bridge', + config: mediaconnect.BridgeConfiguration.egress({ + maxBitrate: mediaconnect.Bitrate.mbps(10), + flowSources: [{ + name: 'cloud-source', + flow: flow, + vpcInterface: vpcInterface, + }], + networkOutputs: [], // Start with empty outputs + }), + gateway: gateway, +}); + +// Add network outputs using the helper method +bridge.addOutput('Output1', { + name: 'on-prem-output-1', + ipAddress: '192.168.1.200', + port: 5001, + networkName: 'production-network', + protocol: mediaconnect.BridgeProtocol.RTP, + ttl: 64, +}); + +bridge.addOutput('Output2', { + name: 'on-prem-output-2', + ipAddress: '192.168.1.201', + port: 5002, + networkName: 'production-network', + protocol: mediaconnect.BridgeProtocol.UDP, +}); +``` + +Interface example of what the Bridge will implement: + +```ts +/** + * Interface for MediaConnect Bridge + */ +export interface IBridge extends IResource, IBridgeRef { + /** + * The name of the bridge + * + * @attribute + */ + readonly bridgeName: string; + + /** + * The Amazon Resource Name (ARN) of the bridge + * + * @attribute + */ + readonly bridgeArn: string; + + /** + * The state of the bridge + * + * @attribute + */ + readonly bridgeState: string; + + /** + * Add a network output to this bridge (for egress bridges only) + */ + addOutput(id: string, options: BridgeOutputOptions): BridgeOutput; + + /** + * Create a CloudWatch metric + * + * @param metricName name of the metric + * @param props metric options + */ + metric(metricName: string, props?: MetricOptions): Metric; + + /** + * Returns Metric for Bridge State + * + * @default - average over 60 seconds + */ + metricBridgeState(props?: MetricOptions): Metric; +} +``` + +Ticking the box below indicates that the public API of this RFC has been signed-off by the API bar raiser (the `status/api-approved` label was applied to the RFC pull request): + +``` +[ ] Signed-off by API Bar Raiser @xxxxx +``` + +## Design Decisions + +### Flow Size Validation + +The construct validates flow size constraints at synthesis time based on the source protocol and NDI configuration: + +- `LARGE_4X` is required for CDI and JPEG XS protocols +- `LARGE` is required when NDI is enabled +- `LARGE_4X` cannot be used with transport stream protocols (RTP, SRT, RIST, etc.) + +These are mutually exclusive — CDI/JPEG XS and NDI cannot coexist on the same flow because they require different flow sizes. + +### Factory Pattern for Configurations + +Router inputs and outputs use factory methods to create configurations: + +- `RouterInputConfiguration.standard()` - Standard protocol-based input +- `RouterInputConfiguration.mediaConnectFlow()` - Input from a specific MediaConnect flow +- `RouterInputConfiguration.mediaConnectFlowWithoutConnection()` - Prepared for flow connection (requires AZ) +- `RouterOutputConfiguration.standard()` - Standard protocol-based output +- `RouterOutputConfiguration.mediaLiveInput()` - Output to a specific MediaLive input +- `RouterOutputConfiguration.mediaLiveInputWithoutConnection()` - Prepared for MediaLive connection (requires AZ) +- `RouterOutputConfiguration.mediaConnectFlow()` - Output to a specific MediaConnect flow +- `RouterOutputConfiguration.mediaConnectFlowWithoutConnection()` - Prepared for flow connection (requires AZ) + +This pattern provides type safety and makes it clear which parameters are required for each configuration type, preventing invalid combinations at compile time. + +### Encryption Patterns + +The construct library uses specific encryption classes for different protocols: + +- **StaticKeyEncryption**: Used for Zixi protocols and flow entitlements (requires algorithm: AES128, AES192, or AES256) +- **SrtPasswordEncryption**: Used for SRT protocols in flow sources and outputs (no algorithm required) +- **RouterSrtEncryption**: Used for SRT protocols in router outputs (simplified structure) +- **TransitEncryption**: Used for securing connections between flows and routers + +**Note on SPEKE**: Flow entitlements do not support SPEKE encryption because flow sources cannot decrypt SPEKE-encrypted content. Only static key encryption is supported for entitlements. + +## Public FAQ + +### What are we launching today? + +We're launching new AWS Elemental MediaConnect L2 Constructs to provide best-practice defaults and developer friendly functions to create your Flow, Bridge, Gateway, Router, and VpcInterface resources. + +The primary aim is to help users with guardrails to improve developer experience, as well as speeding up the development process generally. + +### Why should I use this feature? + +Developers should use this Construct to reduce the amount of boilerplate code, complexity each individual has to navigate, and make it easier to create MediaConnect resources. + +This construct continues our work abstracting AWS Elemental Media Services (following MediaPackageV2). Meaning that we can help builders with compatibility, construction and integration in these services. + +## Internal FAQ + +### Why are we doing this? + +Today we help builders with reference architectures using the opensource project [AWS CDK Media Services Reference Architectures](https://github.com/aws-samples/aws-cdk-mediaservices-refarch). This open source project has received much positive feedback. + +By building out an L2 CDK construct for AWS Elemental MediaConnect (and other services in the future) will mean we can simplify these architectures. + +The existing process requires extensive configuration and lacks standardization, leading to potential errors and a time-consuming setup process. By abstracting and simplifying these resources (L2) we will continue improving our developer experience in AWS CDK. + +### Why should we _not_ do this? + +Users today are already using the L1 construct, and would likely need to do a workflow change and redeployment to alter this existing way of working. + +### What is the technical solution (design) of this feature? + +The main thing required for these resources in particular are abstracting fields using factory patterns. This will make the services more accessible and speed up the development process as you don't need to deep-dive the documentation to understand the resource. + +### Is this a breaking change? + +No - an L2 doesn't exist today. + +### What alternative solutions did you consider? + +**Source/Output configuration: Factory pattern vs. flat props** + +We considered exposing source and output configuration as flat props directly on the Flow construct, similar to how the L1 works. For example: + +```ts +new mediaconnect.Flow(stack, 'Flow', { + sourceName: 'my-source', + sourceProtocol: 'rtp', + sourcePort: 5000, + sourceWhitelistCidr: '203.0.113.0/24', +}); +``` + +- Pros: Simpler API surface, fewer classes to learn +- Cons: No compile-time safety for protocol-specific parameters. Users could supply SRT-specific props (like `minLatency`) alongside an RTP source, which would be silently ignored or cause runtime errors. The L1 already suffers from this problem. + +We chose the factory pattern (`SourceConfiguration.rtp()`, `SourceConfiguration.srtListener()`, etc.) because it makes invalid states unrepresentable at compile time. Each factory method only accepts the parameters valid for that protocol, which eliminates an entire class of configuration errors. + +**Bridge configuration: Single construct vs. separate ingress/egress classes** + +We considered having separate `IngressBridge` and `EgressBridge` classes instead of a single `Bridge` with `BridgeConfiguration.ingress()` / `BridgeConfiguration.egress()`. + +- Pros of separate classes: Stronger type separation, impossible to mix ingress and egress concerns +- Cons of separate classes: Doubles the number of bridge-related classes, and CloudFormation models this as a single `AWS::MediaConnect::Bridge` resource with optional `IngressGatewayBridge` / `EgressGatewayBridge` properties + +We chose the single `Bridge` construct with a configuration factory because it mirrors the CloudFormation model and keeps the API surface smaller, while still providing type safety through the factory methods. + +### What are the drawbacks of this solution? + +- **Larger API surface**: The factory pattern for sources, outputs, and bridge configurations introduces more classes and static methods than a flat-props approach. Users need to discover the right factory method for their protocol, which adds a learning curve compared to a simple props object. +- **Migration from L1**: Users with existing L1-based MediaConnect stacks will need to refactor their code to use the L2 API. While this is not a breaking change (the L1 continues to work), the migration effort could be non-trivial for complex workflows with many flows and bridges. + +### What is the high-level project plan? + +A draft implementation covering all constructs (Flow, Bridge, Gateway, Router, VpcInterface) has been completed. The remaining work is to finalize the RFC review, address any Bar Raiser feedback, and prepare the implementation PR for submission. + +### Are there any open issues that need to be addressed later? + +N/A From a86a3f415d974116a037d40ca4561ca012a920a0 Mon Sep 17 00:00:00 2001 From: Jamie Mullan <46033424+jamiepmullan@users.noreply.github.com> Date: Thu, 19 Mar 2026 19:57:02 +0000 Subject: [PATCH 2/5] linting fixes fix ordering --- text/0884-aws-elemental-mediaconnect-l2.md | 389 +++++++++++++-------- 1 file changed, 242 insertions(+), 147 deletions(-) diff --git a/text/0884-aws-elemental-mediaconnect-l2.md b/text/0884-aws-elemental-mediaconnect-l2.md index e9f87ff97..1d40a1846 100644 --- a/text/0884-aws-elemental-mediaconnect-l2.md +++ b/text/0884-aws-elemental-mediaconnect-l2.md @@ -117,13 +117,26 @@ The rest of this doc outlines the design for an L2 construct. ### README -[AWS Elemental MediaConnect](https://aws.amazon.com/mediaconnect/) is a high-quality transport service for live video that provides the reliability and security of satellite and fiber-optic combined with the flexibility, agility, and economics of IP-based networks. MediaConnect enables you to build mission-critical live video workflows in a fraction of the time and cost of satellite or fiber services. - -Without an L2 construct, developers define MediaConnect flows and bridges via the AWS console, the AWS CLI, and Infrastructure as Code tools like CloudFormation and CDK. - -However, there are several challenges to defining MediaConnect resources at scale that an L2 construct can resolve. For example, developers must reference documentation to determine the valid combinations of parameters for different source types and bridge configurations. - -We could greatly simplify the developer experience in CDK by introducing MediaConnect L2 constructs. This will have a mixture of sensible defaults as well as methods to help build correct configurations of Flows, Bridges, Gateways, Routers, and VPC interfaces. +[AWS Elemental MediaConnect](https://aws.amazon.com/mediaconnect/) is a +high-quality transport service for live video that provides the reliability +and security of satellite and fiber-optic combined with the flexibility, +agility, and economics of IP-based networks. MediaConnect enables you to +build mission-critical live video workflows in a fraction of the time and +cost of satellite or fiber services. + +Without an L2 construct, developers define MediaConnect flows and bridges +via the AWS console, the AWS CLI, and Infrastructure as Code tools like +CloudFormation and CDK. + +However, there are several challenges to defining MediaConnect resources at +scale that an L2 construct can resolve. For example, developers must +reference documentation to determine the valid combinations of parameters +for different source types and bridge configurations. + +We could greatly simplify the developer experience in CDK by introducing +MediaConnect L2 constructs. This will have a mixture of sensible defaults +as well as methods to help build correct configurations of Flows, Bridges, +Gateways, Routers, and VPC interfaces. * [What is AWS Elemental MediaConnect?](https://aws.amazon.com/mediaconnect/) * [MediaConnect Documentation](https://docs.aws.amazon.com/mediaconnect/latest/ug/what-is.html) @@ -131,7 +144,9 @@ We could greatly simplify the developer experience in CDK by introducing MediaCo #### AWS Elemental MediaConnect Flow -A MediaConnect flow represents a transport stream connection between a source and one or more outputs. Flows are the primary resource for transporting live video content. +A MediaConnect flow represents a transport stream connection between a +source and one or more outputs. Flows are the primary resource for +transporting live video content. For further information refer to [our documentation](https://docs.aws.amazon.com/mediaconnect/latest/ug/flows.html). @@ -160,7 +175,9 @@ new mediaconnect.Flow(stack, 'MyFlow', { ##### Adding Outputs to Flows -The `addOutput()` method provides a convenient way to create outputs directly associated with a flow. Outputs define where and how the flow sends content to downstream destinations. +The `addOutput()` method provides a convenient way to create outputs directly +associated with a flow. Outputs define where and how the flow sends content +to downstream destinations. ```ts const flow = new mediaconnect.Flow(stack, 'MyFlow', { @@ -420,7 +437,11 @@ new mediaconnect.Flow(stack, 'MyFlow', { #### AWS Elemental MediaConnect VPC Interface -VPC interfaces enable MediaConnect flows to send and receive content through your Amazon VPC, providing secure, private connectivity between MediaConnect and your VPC resources. VPC interfaces are essential for integrating MediaConnect with other AWS services within your VPC, such as connecting flows to bridges or routing content through private networks. +VPC interfaces enable MediaConnect flows to send and receive content through +your Amazon VPC, providing secure, private connectivity between MediaConnect +and your VPC resources. VPC interfaces are essential for integrating +MediaConnect with other AWS services within your VPC, such as connecting +flows to bridges or routing content through private networks. For further information refer to [our documentation](https://docs.aws.amazon.com/mediaconnect/latest/ug/vpc-interfaces.html). @@ -487,7 +508,10 @@ const vpcInterface = mediaconnect.VpcInterface.create({ }); ``` -**Note:** You cannot specify both `networkInterfaceType` and `networkInterfaceIds`. Use `networkInterfaceType` to let MediaConnect create interfaces automatically, or `networkInterfaceIds` to use existing interfaces. +**Note:** You cannot specify both `networkInterfaceType` and +`networkInterfaceIds`. Use `networkInterfaceType` to let MediaConnect +create interfaces automatically, or `networkInterfaceIds` to use +existing interfaces. ##### Using VPC Interfaces with Flows @@ -619,7 +643,10 @@ export interface VpcInterfaceConfig { #### AWS Elemental MediaConnect Gateway -MediaConnect gateways enable hybrid cloud workflows by allowing on-premises equipment to connect to AWS cloud resources. Gateways define the network infrastructure that bridges use to transport video between on-premises and cloud environments. +MediaConnect gateways enable hybrid cloud workflows by allowing on-premises +equipment to connect to AWS cloud resources. Gateways define the network +infrastructure that bridges use to transport video between on-premises and +cloud environments. For further information refer to [our documentation](https://docs.aws.amazon.com/mediaconnect/latest/ug/gateways.html). @@ -725,7 +752,10 @@ export interface IGateway extends IResource, IGatewayRef { #### AWS Elemental MediaConnect Bridge -MediaConnect bridges enable you to interconnect on-premises equipment with cloud-based workflows. Bridges support both ingress (on-premises to cloud) and egress (cloud to on-premises) scenarios. Bridges must be associated with a gateway to function. +MediaConnect bridges enable you to interconnect on-premises equipment with +cloud-based workflows. Bridges support both ingress (on-premises to cloud) +and egress (cloud to on-premises) scenarios. Bridges must be associated +with a gateway to function. For further information refer to [our documentation](https://docs.aws.amazon.com/mediaconnect/latest/ug/bridges.html). @@ -767,7 +797,7 @@ BridgeProps { } ``` -###### Ingress Bridge (On-premises to Cloud) +##### Ingress Bridge (On-premises to Cloud) ```ts const gateway = new mediaconnect.Gateway(stack, 'MyGateway', { @@ -796,7 +826,7 @@ new mediaconnect.Bridge(stack, 'MyIngressBridge', { }); ``` -###### Egress Bridge (Cloud to On-premises) +##### Egress Bridge (Cloud to On-premises) ```ts declare const gateway: mediaconnect.IGateway; @@ -827,9 +857,101 @@ new mediaconnect.Bridge(stack, 'MyEgressBridge', { }); ``` +##### Adding Outputs to Bridges + +For egress bridges, the `addOutput()` method provides a convenient +way to add network outputs: + +```ts +const bridge = new mediaconnect.Bridge(stack, 'MyEgressBridge', { + bridgeName: 'my-egress-bridge', + config: mediaconnect.BridgeConfiguration.egress({ + maxBitrate: mediaconnect.Bitrate.mbps(10), + flowSources: [{ + name: 'cloud-source', + flow: flow, + vpcInterface: vpcInterface, + }], + networkOutputs: [], // Start with empty outputs + }), + gateway: gateway, +}); + +// Add network outputs using the helper method +bridge.addOutput('Output1', { + name: 'on-prem-output-1', + ipAddress: '192.168.1.200', + port: 5001, + networkName: 'production-network', + protocol: mediaconnect.BridgeProtocol.RTP, + ttl: 64, +}); + +bridge.addOutput('Output2', { + name: 'on-prem-output-2', + ipAddress: '192.168.1.201', + port: 5002, + networkName: 'production-network', + protocol: mediaconnect.BridgeProtocol.UDP, +}); +``` + +Interface example of what the Bridge will implement: + +```ts +/** + * Interface for MediaConnect Bridge + */ +export interface IBridge extends IResource, IBridgeRef { + /** + * The name of the bridge + * + * @attribute + */ + readonly bridgeName: string; + + /** + * The Amazon Resource Name (ARN) of the bridge + * + * @attribute + */ + readonly bridgeArn: string; + + /** + * The state of the bridge + * + * @attribute + */ + readonly bridgeState: string; + + /** + * Add a network output to this bridge (for egress bridges only) + */ + addOutput(id: string, options: BridgeOutputOptions): BridgeOutput; + + /** + * Create a CloudWatch metric + * + * @param metricName name of the metric + * @param props metric options + */ + metric(metricName: string, props?: MetricOptions): Metric; + + /** + * Returns Metric for Bridge State + * + * @default - average over 60 seconds + */ + metricBridgeState(props?: MetricOptions): Metric; +} +``` + #### AWS Elemental MediaConnect Router -MediaConnect routers provide high-performance, low-latency video routing capabilities for building complex live video workflows. Router resources include network interfaces, inputs, and outputs. +AWS Elemental MediaConnect Router enables real-time routing of live +video streams between sources and destinations, modernizing broadcast +infrastructure through cloud-native flexibility and scalability. Router +resources include network interfaces, inputs, and outputs. For further information refer to [our documentation](https://docs.aws.amazon.com/mediaconnect/latest/ug/routers.html). @@ -982,95 +1104,9 @@ const flowOutputNoConnection = new mediaconnect.RouterOutput(stack, 'FlowOutputN }); ``` -##### Adding Outputs to Bridges - -For egress bridges, the `addOutput()` method provides a convenient way to add network outputs: - -```ts -const bridge = new mediaconnect.Bridge(stack, 'MyEgressBridge', { - bridgeName: 'my-egress-bridge', - config: mediaconnect.BridgeConfiguration.egress({ - maxBitrate: mediaconnect.Bitrate.mbps(10), - flowSources: [{ - name: 'cloud-source', - flow: flow, - vpcInterface: vpcInterface, - }], - networkOutputs: [], // Start with empty outputs - }), - gateway: gateway, -}); - -// Add network outputs using the helper method -bridge.addOutput('Output1', { - name: 'on-prem-output-1', - ipAddress: '192.168.1.200', - port: 5001, - networkName: 'production-network', - protocol: mediaconnect.BridgeProtocol.RTP, - ttl: 64, -}); - -bridge.addOutput('Output2', { - name: 'on-prem-output-2', - ipAddress: '192.168.1.201', - port: 5002, - networkName: 'production-network', - protocol: mediaconnect.BridgeProtocol.UDP, -}); -``` - -Interface example of what the Bridge will implement: - -```ts -/** - * Interface for MediaConnect Bridge - */ -export interface IBridge extends IResource, IBridgeRef { - /** - * The name of the bridge - * - * @attribute - */ - readonly bridgeName: string; - - /** - * The Amazon Resource Name (ARN) of the bridge - * - * @attribute - */ - readonly bridgeArn: string; - - /** - * The state of the bridge - * - * @attribute - */ - readonly bridgeState: string; - - /** - * Add a network output to this bridge (for egress bridges only) - */ - addOutput(id: string, options: BridgeOutputOptions): BridgeOutput; - - /** - * Create a CloudWatch metric - * - * @param metricName name of the metric - * @param props metric options - */ - metric(metricName: string, props?: MetricOptions): Metric; - - /** - * Returns Metric for Bridge State - * - * @default - average over 60 seconds - */ - metricBridgeState(props?: MetricOptions): Metric; -} -``` - -Ticking the box below indicates that the public API of this RFC has been signed-off by the API bar raiser (the `status/api-approved` label was applied to the RFC pull request): +Ticking the box below indicates that the public API of this RFC has been +signed-off by the API bar raiser (the `status/api-approved` label was +applied to the RFC pull request): ``` [ ] Signed-off by API Bar Raiser @xxxxx @@ -1080,63 +1116,93 @@ Ticking the box below indicates that the public API of this RFC has been signed- ### Flow Size Validation -The construct validates flow size constraints at synthesis time based on the source protocol and NDI configuration: +The construct validates flow size constraints at synthesis time based on +the source protocol and NDI configuration: -- `LARGE_4X` is required for CDI and JPEG XS protocols -- `LARGE` is required when NDI is enabled -- `LARGE_4X` cannot be used with transport stream protocols (RTP, SRT, RIST, etc.) +- `MEDIUM` supports transport stream protocols (RTP, SRT, RIST, etc.) + but not NDI or CDI +- `LARGE` supports transport streams and NDI, and is required when NDI + is enabled +- `LARGE_4X` is required for CDI and JPEG XS protocols, and does not + support transport streams or NDI -These are mutually exclusive — CDI/JPEG XS and NDI cannot coexist on the same flow because they require different flow sizes. +These are mutually exclusive — CDI/JPEG XS and NDI cannot coexist on +the same flow because they require different flow sizes. ### Factory Pattern for Configurations Router inputs and outputs use factory methods to create configurations: - `RouterInputConfiguration.standard()` - Standard protocol-based input -- `RouterInputConfiguration.mediaConnectFlow()` - Input from a specific MediaConnect flow -- `RouterInputConfiguration.mediaConnectFlowWithoutConnection()` - Prepared for flow connection (requires AZ) -- `RouterOutputConfiguration.standard()` - Standard protocol-based output -- `RouterOutputConfiguration.mediaLiveInput()` - Output to a specific MediaLive input -- `RouterOutputConfiguration.mediaLiveInputWithoutConnection()` - Prepared for MediaLive connection (requires AZ) -- `RouterOutputConfiguration.mediaConnectFlow()` - Output to a specific MediaConnect flow -- `RouterOutputConfiguration.mediaConnectFlowWithoutConnection()` - Prepared for flow connection (requires AZ) - -This pattern provides type safety and makes it clear which parameters are required for each configuration type, preventing invalid combinations at compile time. +- `RouterInputConfiguration.mediaConnectFlow()` - Input from a specific + MediaConnect flow +- `RouterInputConfiguration.mediaConnectFlowWithoutConnection()` - Prepared + for flow connection (requires AZ) +- `RouterOutputConfiguration.standard()` - Standard protocol-based output +- `RouterOutputConfiguration.mediaLiveInput()` - Output to a specific + MediaLive input +- `RouterOutputConfiguration.mediaLiveInputWithoutConnection()` - Prepared + for MediaLive connection (requires AZ) +- `RouterOutputConfiguration.mediaConnectFlow()` - Output to a specific + MediaConnect flow +- `RouterOutputConfiguration.mediaConnectFlowWithoutConnection()` - Prepared + for flow connection (requires AZ) + +This pattern provides type safety and makes it clear which parameters are +required for each configuration type, preventing invalid combinations at +compile time. ### Encryption Patterns The construct library uses specific encryption classes for different protocols: -- **StaticKeyEncryption**: Used for Zixi protocols and flow entitlements (requires algorithm: AES128, AES192, or AES256) -- **SrtPasswordEncryption**: Used for SRT protocols in flow sources and outputs (no algorithm required) -- **RouterSrtEncryption**: Used for SRT protocols in router outputs (simplified structure) -- **TransitEncryption**: Used for securing connections between flows and routers +- **StaticKeyEncryption**: Used for Zixi protocols and flow entitlements + (requires algorithm: AES128, AES192, or AES256) +- **SrtPasswordEncryption**: Used for SRT protocols in flow sources and + outputs (no algorithm required) +- **RouterSrtEncryption**: Used for SRT protocols in router outputs + (simplified structure) +- **TransitEncryption**: Used for securing connections between flows + and routers -**Note on SPEKE**: Flow entitlements do not support SPEKE encryption because flow sources cannot decrypt SPEKE-encrypted content. Only static key encryption is supported for entitlements. +**Note on SPEKE**: Flow entitlements do not support SPEKE encryption +because flow sources cannot decrypt SPEKE-encrypted content. Only static +key encryption is supported for entitlements. ## Public FAQ ### What are we launching today? -We're launching new AWS Elemental MediaConnect L2 Constructs to provide best-practice defaults and developer friendly functions to create your Flow, Bridge, Gateway, Router, and VpcInterface resources. +We're launching new AWS Elemental MediaConnect L2 Constructs to provide +best-practice defaults and developer friendly functions to create your +Flow, Bridge, Gateway, Router, and VpcInterface resources. The primary aim is to help users with guardrails to improve developer experience, as well as speeding up the development process generally. ### Why should I use this feature? -Developers should use this Construct to reduce the amount of boilerplate code, complexity each individual has to navigate, and make it easier to create MediaConnect resources. +Developers should use this Construct to reduce the amount of boilerplate +code, complexity each individual has to navigate, and make it easier to +create MediaConnect resources. -This construct continues our work abstracting AWS Elemental Media Services (following MediaPackageV2). Meaning that we can help builders with compatibility, construction and integration in these services. +This construct continues our work abstracting AWS Elemental Media Services +(following MediaPackageV2). Meaning that we can help builders with +compatibility, construction and integration in these services. ## Internal FAQ ### Why are we doing this? -Today we help builders with reference architectures using the opensource project [AWS CDK Media Services Reference Architectures](https://github.com/aws-samples/aws-cdk-mediaservices-refarch). This open source project has received much positive feedback. +Today we help builders with reference architectures using the opensource +project [AWS CDK Media Services Reference Architectures](https://github.com/aws-samples/aws-cdk-mediaservices-refarch). +This open source project has received much positive feedback. By building out an L2 CDK construct for AWS Elemental MediaConnect (and other services in the future) will mean we can simplify these architectures. -The existing process requires extensive configuration and lacks standardization, leading to potential errors and a time-consuming setup process. By abstracting and simplifying these resources (L2) we will continue improving our developer experience in AWS CDK. +The existing process requires extensive configuration and lacks +standardization, leading to potential errors and a time-consuming setup +process. By abstracting and simplifying these resources (L2) we will +continue improving our developer experience in AWS CDK. ### Why should we _not_ do this? @@ -1144,7 +1210,10 @@ Users today are already using the L1 construct, and would likely need to do a wo ### What is the technical solution (design) of this feature? -The main thing required for these resources in particular are abstracting fields using factory patterns. This will make the services more accessible and speed up the development process as you don't need to deep-dive the documentation to understand the resource. +The main thing required for these resources in particular are abstracting +fields using factory patterns. This will make the services more accessible +and speed up the development process as you don't need to deep-dive the +documentation to understand the resource. ### Is this a breaking change? @@ -1152,9 +1221,10 @@ No - an L2 doesn't exist today. ### What alternative solutions did you consider? -**Source/Output configuration: Factory pattern vs. flat props** +#### Source/Output configuration: Factory pattern vs. flat props -We considered exposing source and output configuration as flat props directly on the Flow construct, similar to how the L1 works. For example: +We considered exposing source and output configuration as flat props +directly on the Flow construct, similar to how the L1 works. For example: ```ts new mediaconnect.Flow(stack, 'Flow', { @@ -1166,27 +1236,52 @@ new mediaconnect.Flow(stack, 'Flow', { ``` - Pros: Simpler API surface, fewer classes to learn -- Cons: No compile-time safety for protocol-specific parameters. Users could supply SRT-specific props (like `minLatency`) alongside an RTP source, which would be silently ignored or cause runtime errors. The L1 already suffers from this problem. - -We chose the factory pattern (`SourceConfiguration.rtp()`, `SourceConfiguration.srtListener()`, etc.) because it makes invalid states unrepresentable at compile time. Each factory method only accepts the parameters valid for that protocol, which eliminates an entire class of configuration errors. - -**Bridge configuration: Single construct vs. separate ingress/egress classes** - -We considered having separate `IngressBridge` and `EgressBridge` classes instead of a single `Bridge` with `BridgeConfiguration.ingress()` / `BridgeConfiguration.egress()`. - -- Pros of separate classes: Stronger type separation, impossible to mix ingress and egress concerns -- Cons of separate classes: Doubles the number of bridge-related classes, and CloudFormation models this as a single `AWS::MediaConnect::Bridge` resource with optional `IngressGatewayBridge` / `EgressGatewayBridge` properties - -We chose the single `Bridge` construct with a configuration factory because it mirrors the CloudFormation model and keeps the API surface smaller, while still providing type safety through the factory methods. +- Cons: No compile-time safety for protocol-specific parameters. Users + could supply SRT-specific props (like `minLatency`) alongside an RTP + source, which would be silently ignored or cause runtime errors. The L1 + already suffers from this problem. + +We chose the factory pattern (`SourceConfiguration.rtp()`, +`SourceConfiguration.srtListener()`, etc.) because it makes invalid states +unrepresentable at compile time. Each factory method only accepts the +parameters valid for that protocol, which eliminates an entire class of +configuration errors. + +#### Bridge configuration: Single construct vs. separate ingress/egress classes + +We considered having separate `IngressBridge` and `EgressBridge` classes +instead of a single `Bridge` with `BridgeConfiguration.ingress()` / +`BridgeConfiguration.egress()`. + +- Pros of separate classes: Stronger type separation, impossible to mix + ingress and egress concerns +- Cons of separate classes: Doubles the number of bridge-related classes, + and CloudFormation models this as a single `AWS::MediaConnect::Bridge` + resource with optional `IngressGatewayBridge` / `EgressGatewayBridge` + properties + +We chose the single `Bridge` construct with a configuration factory +because it mirrors the CloudFormation model and keeps the API surface +smaller, while still providing type safety through the factory methods. ### What are the drawbacks of this solution? -- **Larger API surface**: The factory pattern for sources, outputs, and bridge configurations introduces more classes and static methods than a flat-props approach. Users need to discover the right factory method for their protocol, which adds a learning curve compared to a simple props object. -- **Migration from L1**: Users with existing L1-based MediaConnect stacks will need to refactor their code to use the L2 API. While this is not a breaking change (the L1 continues to work), the migration effort could be non-trivial for complex workflows with many flows and bridges. +- **Larger API surface**: The factory pattern for sources, outputs, and + bridge configurations introduces more classes and static methods than a + flat-props approach. Users need to discover the right factory method for + their protocol, which adds a learning curve compared to a simple props + object. +- **Migration from L1**: Users with existing L1-based MediaConnect stacks + will need to refactor their code to use the L2 API. While this is not a + breaking change (the L1 continues to work), the migration effort could + be non-trivial for complex workflows with many flows and bridges. ### What is the high-level project plan? -A draft implementation covering all constructs (Flow, Bridge, Gateway, Router, VpcInterface) has been completed. The remaining work is to finalize the RFC review, address any Bar Raiser feedback, and prepare the implementation PR for submission. +A draft implementation covering all constructs (Flow, Bridge, Gateway, +Router, VpcInterface) has been completed. The remaining work is to +finalize the RFC review, address any Bar Raiser feedback, and prepare +the implementation PR for submission. ### Are there any open issues that need to be addressed later? From caaca2cd7a32242159b84eedd4852ca50d77bac3 Mon Sep 17 00:00:00 2001 From: Jamie Mullan <46033424+jamiepmullan@users.noreply.github.com> Date: Tue, 7 Apr 2026 10:30:50 +0100 Subject: [PATCH 3/5] MediaConnect RFC fixes --- text/0884-aws-elemental-mediaconnect-l2.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/text/0884-aws-elemental-mediaconnect-l2.md b/text/0884-aws-elemental-mediaconnect-l2.md index 1d40a1846..477d21721 100644 --- a/text/0884-aws-elemental-mediaconnect-l2.md +++ b/text/0884-aws-elemental-mediaconnect-l2.md @@ -4,7 +4,7 @@ * **Original Author(s):** @jamiepmullan * **Tracking Issue:** [#884](https://github.com/aws/aws-cdk-rfcs/issues/884) -* **API Bar Raiser:** TBD +* **API Bar Raiser:** @alvazjor This design outlines how we build an L2 construct for AWS Elemental MediaConnect, delivering the following benefits: @@ -82,7 +82,7 @@ const flow = new mediaconnect.Flow(stack, 'Flow', { source: mediaconnect.SourceConfiguration.rtp({ flowSourceName: 'my-source', port: 5000, - network: mediaconnect.NetworkConfiguration.internet('203.0.113.0/24'), + network: mediaconnect.NetworkConfiguration.publicNetwork('203.0.113.0/24'), }), vpcInterfaces: [vpcInterface], }); @@ -155,7 +155,7 @@ new mediaconnect.Flow(stack, 'MyFlow', { source: mediaconnect.SourceConfiguration.rtp({ flowSourceName: 'my-source', port: 5000, - network: mediaconnect.NetworkConfiguration.internet('203.0.113.0/24'), + network: mediaconnect.NetworkConfiguration.publicNetwork('203.0.113.0/24'), }), }); ``` @@ -168,7 +168,7 @@ new mediaconnect.Flow(stack, 'MyFlow', { source: mediaconnect.SourceConfiguration.rtp({ flowSourceName: 'my-source', port: 5000, - network: mediaconnect.NetworkConfiguration.internet('203.0.113.0/24'), + network: mediaconnect.NetworkConfiguration.publicNetwork('203.0.113.0/24'), }), }); ``` @@ -184,7 +184,7 @@ const flow = new mediaconnect.Flow(stack, 'MyFlow', { source: mediaconnect.SourceConfiguration.rtp({ flowSourceName: 'my-source', port: 5000, - network: mediaconnect.NetworkConfiguration.internet('203.0.113.0/24'), + network: mediaconnect.NetworkConfiguration.publicNetwork('203.0.113.0/24'), }), }); @@ -389,7 +389,7 @@ new mediaconnect.Flow(stack, 'MyFlow', { description: 'Live encoder feed', port: 5000, minLatency: Duration.millis(2000), - network: mediaconnect.NetworkConfiguration.internet('203.0.113.0/24'), + network: mediaconnect.NetworkConfiguration.publicNetwork('203.0.113.0/24'), }), }); ``` @@ -963,7 +963,7 @@ Network interfaces define the network connectivity for router inputs and outputs // Public network interface const publicInterface = new mediaconnect.RouterNetworkInterface(stack, 'PublicInterface', { routerNetworkInterfaceName: 'public-interface', - configuration: mediaconnect.RouterNetworkConfiguration.internet({ + configuration: mediaconnect.RouterNetworkConfiguration.publicNetwork({ cidr: ['10.0.0.0/16'], }), }); From 921cda14d177549b9322b278eab94d00caac33f7 Mon Sep 17 00:00:00 2001 From: Jamie Mullan <46033424+jamiepmullan@users.noreply.github.com> Date: Mon, 13 Apr 2026 16:31:26 +0100 Subject: [PATCH 4/5] design fixes --- text/0884-aws-elemental-mediaconnect-l2.md | 224 ++++++++++++++++++--- 1 file changed, 191 insertions(+), 33 deletions(-) diff --git a/text/0884-aws-elemental-mediaconnect-l2.md b/text/0884-aws-elemental-mediaconnect-l2.md index 477d21721..9b01ab3f2 100644 --- a/text/0884-aws-elemental-mediaconnect-l2.md +++ b/text/0884-aws-elemental-mediaconnect-l2.md @@ -91,7 +91,7 @@ const bridge = new mediaconnect.Bridge(stack, 'Bridge', { bridgeName: 'my-bridge', gateway: gateway, config: mediaconnect.BridgeConfiguration.egress({ - maxBitrate: mediaconnect.Bitrate.mbps(10), + maxBitrate: cdk.Bitrate.mbps(10), flowSources: [{ name: 'flow-source', flow: flow, @@ -356,24 +356,47 @@ export interface IFlow extends IResource, IFlowRef { * @default - maximum over 60 seconds */ metricFailoverSourceSelected(props?: MetricOptions): Metric; + + /** + * Grant methods for this flow + */ + readonly grants: FlowGrants; } ``` Provide fromMethod capability - allowing imports of the resource: ```ts -/** - * Creates a Flow construct that represents an external (imported) Flow - */ -public static fromFlowAttributes(scope: Construct, id: string, attrs: FlowAttributes): IFlow { - class Import extends FlowBase implements IFlow { - public readonly flowArn = attrs.flowArn; - public readonly sourceArn = attrs.primarySourceArn; - public readonly isFailoverEnabled = attrs.isFailoverEnabled ?? false; - } - - return new Import(scope, id); -} +// Simple import — when you only need the flow ARN +const importedFlow = mediaconnect.Flow.fromFlowArn( + stack, 'ImportedFlow', + 'arn:aws:mediaconnect:us-west-2:111122223333:flow:1-11111111111111111111111111111111:MyFlow', +); + +// Full import — when you need source ARN or failover state +const importedFlow = mediaconnect.Flow.fromFlowAttributes(stack, 'ImportedFlow', { + flowArn: 'arn:aws:mediaconnect:us-west-2:111122223333:flow:1-11111111111111111111111111111111:MyFlow', + sourceArn: 'arn:aws:mediaconnect:us-west-2:111122223333:source:2-22222222222222222222222222222222:MySource', + isFailoverEnabled: true, +}); +``` + +##### Granting Permissions on Flows + +Grant methods enable Lambda functions or Step Functions to orchestrate flow lifecycle without manual IAM policy wiring: + +```ts +declare const flow: mediaconnect.IFlow; +declare const fn: lambda.IFunction; + +// Grant permission to start the flow +flow.grants.start(fn); + +// Grant permission to stop the flow +flow.grants.stop(fn); + +// Grant arbitrary actions +flow.grants.actions(fn, 'mediaconnect:UpdateFlow'); ``` ##### Flow Source Types @@ -424,9 +447,10 @@ new mediaconnect.Flow(stack, 'MyFlow', { ```ts // Import an entitlement from another AWS account -const entitlement = mediaconnect.FlowEntitlement.fromFlowEntitlementAttributes(stack, 'ImportedEntitlement', { - entitlementArn: 'arn:aws:mediaconnect:us-west-2:111122223333:entitlement:1-11111111111111111111111111111111:MyEntitlement', -}); +const entitlement = mediaconnect.FlowEntitlement.fromFlowEntitlementArn( + stack, 'ImportedEntitlement', + 'arn:aws:mediaconnect:us-west-2:111122223333:entitlement:1-11111111111111111111111111111111:MyEntitlement', +); new mediaconnect.Flow(stack, 'MyFlow', { source: mediaconnect.SourceConfiguration.entitlement({ @@ -435,6 +459,69 @@ new mediaconnect.Flow(stack, 'MyFlow', { }); ``` +#### AWS Elemental MediaConnect Flow Source + +A FlowSource (`AWS::MediaConnect::FlowSource`) is a standalone resource used to add secondary +or failover sources to an existing flow. This is separate from the primary source defined in +`FlowProps` — secondary sources are created as their own constructs and associated with a flow +to enable failover configurations. + +For further information refer to [our documentation](https://docs.aws.amazon.com/mediaconnect/latest/ug/flows-source.html). + +```ts +declare const flow: mediaconnect.IFlow; + +new mediaconnect.FlowSource(stack, 'BackupSource', { + flow: flow, + flowSourceName: 'backup-source', + source: mediaconnect.SourceConfiguration.srtListener({ + flowSourceName: 'backup', + port: 5001, + network: mediaconnect.NetworkConfiguration.publicNetwork('203.0.113.0/24'), + }), +}); +``` + +This enables failover configurations where the primary source is defined on the Flow and secondary sources are added as separate constructs. + +#### AWS Elemental MediaConnect Flow Entitlement + +A FlowEntitlement (`AWS::MediaConnect::FlowEntitlement`) grants another AWS account permission +to access content from your flow. It is created on the content originator's side and consumed +by the subscriber via `SourceConfiguration.entitlement()`. + +For further information refer to [our documentation](https://docs.aws.amazon.com/mediaconnect/latest/ug/entitlements.html). + +##### Creating an Entitlement + +```ts +declare const flow: mediaconnect.IFlow; + +const entitlement = new mediaconnect.FlowEntitlement(stack, 'Entitlement', { + flow: flow, + entitlementName: 'partner-access', + subscribers: ['111122223333'], + description: 'Grant partner access to live feed', +}); +``` + +##### Consuming an Entitlement (Subscriber Side) + +The subscriber imports the entitlement ARN and uses it as a source in their own flow: + +```ts +const entitlement = mediaconnect.FlowEntitlement.fromFlowEntitlementArn( + stack, 'ImportedEntitlement', + 'arn:aws:mediaconnect:us-west-2:111122223333:entitlement:1-11111111111111111111111111111111:MyEntitlement', +); + +new mediaconnect.Flow(stack, 'SubscriberFlow', { + source: mediaconnect.SourceConfiguration.entitlement({ + entitlement: entitlement, + }), +}); +``` + #### AWS Elemental MediaConnect VPC Interface VPC interfaces enable MediaConnect flows to send and receive content through @@ -553,7 +640,7 @@ new mediaconnect.Bridge(stack, 'EgressBridge', { bridgeName: 'my-egress-bridge', gateway: gateway, config: mediaconnect.BridgeConfiguration.egress({ - maxBitrate: mediaconnect.Bitrate.mbps(10), + maxBitrate: cdk.Bitrate.mbps(10), flowSources: [{ name: 'cloud-source', flow: flow, @@ -692,16 +779,17 @@ gateway.addNetwork({ You can import existing gateways for use in your CDK application: ```ts -const gateway = mediaconnect.Gateway.fromAttributes(stack, 'ImportedGateway', { - gatewayArn: 'arn:aws:mediaconnect:us-west-2:111122223333:gateway:1-11111111111111111111111111111111:MyGateway', -}); +const gateway = mediaconnect.Gateway.fromGatewayArn( + stack, 'ImportedGateway', + 'arn:aws:mediaconnect:us-west-2:111122223333:gateway:1-11111111111111111111111111111111:MyGateway', +); // Use the imported gateway with bridges new mediaconnect.Bridge(stack, 'MyBridge', { bridgeName: 'my-bridge', gateway: gateway, config: mediaconnect.BridgeConfiguration.egress({ - maxBitrate: mediaconnect.Bitrate.mbps(10), + maxBitrate: cdk.Bitrate.mbps(10), flowSources: [/* ... */], networkOutputs: [/* ... */], }), @@ -812,7 +900,7 @@ const gateway = new mediaconnect.Gateway(stack, 'MyGateway', { new mediaconnect.Bridge(stack, 'MyIngressBridge', { bridgeName: 'my-ingress-bridge', config: mediaconnect.BridgeConfiguration.ingress({ - maxBitrate: mediaconnect.Bitrate.mbps(10), + maxBitrate: cdk.Bitrate.mbps(10), maxOutputs: 2, networkSources: [{ name: 'on-prem-source', @@ -836,7 +924,7 @@ declare const vpcInterface: mediaconnect.VpcInterfaceConfig; new mediaconnect.Bridge(stack, 'MyEgressBridge', { bridgeName: 'my-egress-bridge', config: mediaconnect.BridgeConfiguration.egress({ - maxBitrate: mediaconnect.Bitrate.mbps(10), + maxBitrate: cdk.Bitrate.mbps(10), flowSources: [{ name: 'cloud-source', flow: flow, @@ -857,6 +945,17 @@ new mediaconnect.Bridge(stack, 'MyEgressBridge', { }); ``` +##### Importing Existing Bridges + +You can import existing bridges for use in your CDK application: + +```ts +const importedBridge = mediaconnect.Bridge.fromBridgeArn( + stack, 'ImportedBridge', + 'arn:aws:mediaconnect:us-west-2:111122223333:bridge:1-11111111111111111111111111111111:MyBridge', +); +``` + ##### Adding Outputs to Bridges For egress bridges, the `addOutput()` method provides a convenient @@ -866,7 +965,7 @@ way to add network outputs: const bridge = new mediaconnect.Bridge(stack, 'MyEgressBridge', { bridgeName: 'my-egress-bridge', config: mediaconnect.BridgeConfiguration.egress({ - maxBitrate: mediaconnect.Bitrate.mbps(10), + maxBitrate: cdk.Bitrate.mbps(10), flowSources: [{ name: 'cloud-source', flow: flow, @@ -955,6 +1054,30 @@ resources include network interfaces, inputs, and outputs. For further information refer to [our documentation](https://docs.aws.amazon.com/mediaconnect/latest/ug/routers.html). +##### Importing Existing Router Resources + +You can import existing router resources for use in your CDK application: + +```ts +// Import a router network interface +const importedInterface = mediaconnect.RouterNetworkInterface.fromRouterNetworkInterfaceArn( + stack, 'ImportedInterface', + 'arn:aws:mediaconnect:us-west-2:111122223333:router-network-interface:1-11111111111111111111111111111111:MyInterface', +); + +// Import a router input +const importedInput = mediaconnect.RouterInput.fromRouterInputArn( + stack, 'ImportedInput', + 'arn:aws:mediaconnect:us-west-2:111122223333:router-input:1-11111111111111111111111111111111:MyInput', +); + +// Import a router output +const importedOutput = mediaconnect.RouterOutput.fromRouterOutputArn( + stack, 'ImportedOutput', + 'arn:aws:mediaconnect:us-west-2:111122223333:router-output:1-11111111111111111111111111111111:MyOutput', +); +``` + ##### Router Network Interfaces Network interfaces define the network connectivity for router inputs and outputs: @@ -964,7 +1087,7 @@ Network interfaces define the network connectivity for router inputs and outputs const publicInterface = new mediaconnect.RouterNetworkInterface(stack, 'PublicInterface', { routerNetworkInterfaceName: 'public-interface', configuration: mediaconnect.RouterNetworkConfiguration.publicNetwork({ - cidr: ['10.0.0.0/16'], + cidr: ['203.0.113.0/24'], }), }); @@ -991,7 +1114,7 @@ declare const networkInterface: mediaconnect.IRouterNetworkInterface; const input = new mediaconnect.RouterInput(stack, 'RtpInput', { routerInputName: 'rtp-input', - maximumBitrate: mediaconnect.Bitrate.mbps(10), + maximumBitrate: cdk.Bitrate.mbps(10), routingScope: mediaconnect.RoutingScope.REGIONAL, tier: mediaconnect.RouterInputTier.INPUT_100, availabilityZone: 'us-east-1a', @@ -1009,7 +1132,7 @@ declare const flowOutput: mediaconnect.IFlowOutput; const flowInput = new mediaconnect.RouterInput(stack, 'FlowInput', { routerInputName: 'flow-input', - maximumBitrate: mediaconnect.Bitrate.mbps(20), + maximumBitrate: cdk.Bitrate.mbps(20), routingScope: mediaconnect.RoutingScope.REGIONAL, tier: mediaconnect.RouterInputTier.INPUT_50, availabilityZone: 'us-east-1a', @@ -1022,7 +1145,7 @@ const flowInput = new mediaconnect.RouterInput(stack, 'FlowInput', { // MediaConnect Flow input without specific connection (requires explicit AZ) const flowInputNoConnection = new mediaconnect.RouterInput(stack, 'FlowInputNoConnection', { routerInputName: 'flow-input-no-connection', - maximumBitrate: mediaconnect.Bitrate.mbps(20), + maximumBitrate: cdk.Bitrate.mbps(20), routingScope: mediaconnect.RoutingScope.REGIONAL, tier: mediaconnect.RouterInputTier.INPUT_50, availabilityZone: 'us-east-1a', @@ -1032,6 +1155,41 @@ const flowInputNoConnection = new mediaconnect.RouterInput(stack, 'FlowInputNoCo }); ``` +##### Router Input Grants + +Grant methods enable orchestration of router input lifecycle: + +```ts +declare const routerInput: mediaconnect.IRouterInput; +declare const fn: lambda.IFunction; + +routerInput.grants.start(fn); +routerInput.grants.stop(fn); +routerInput.grants.restart(fn); +routerInput.grants.actions(fn, 'mediaconnect:UpdateRouterInput'); +``` + +Interface for Router Input: + +```ts +/** + * Interface for MediaConnect Router Input + */ +export interface IRouterInput extends IResource { + /** + * The Amazon Resource Name (ARN) of the router input + * + * @attribute + */ + readonly routerInputArn: string; + + /** + * Grant methods for this router input + */ + readonly grants: RouterInputGrants; +} +``` + ##### Router Outputs Router outputs send video streams to various destinations: @@ -1042,7 +1200,7 @@ declare const networkInterface: mediaconnect.IRouterNetworkInterface; const output = new mediaconnect.RouterOutput(stack, 'SrtOutput', { routerOutputName: 'srt-output', - maximumBitrate: mediaconnect.Bitrate.mbps(10), + maximumBitrate: cdk.Bitrate.mbps(10), routingScope: mediaconnect.RoutingScope.REGIONAL, tier: mediaconnect.RouterOutputTier.OUTPUT_100, configuration: mediaconnect.RouterOutputConfiguration.standard({ @@ -1059,7 +1217,7 @@ declare const mediaLiveInput: medialive.CfnInput; const mediaLiveOutput = new mediaconnect.RouterOutput(stack, 'MediaLiveOutput', { routerOutputName: 'medialive-output', - maximumBitrate: mediaconnect.Bitrate.mbps(15), + maximumBitrate: cdk.Bitrate.mbps(15), routingScope: mediaconnect.RoutingScope.GLOBAL, tier: mediaconnect.RouterOutputTier.OUTPUT_50, configuration: mediaconnect.RouterOutputConfiguration.mediaLiveInput({ @@ -1071,7 +1229,7 @@ const mediaLiveOutput = new mediaconnect.RouterOutput(stack, 'MediaLiveOutput', // MediaLive output without specific connection (requires explicit AZ) const mediaLiveOutputNoConnection = new mediaconnect.RouterOutput(stack, 'MediaLiveOutputNoConnection', { routerOutputName: 'medialive-output-no-connection', - maximumBitrate: mediaconnect.Bitrate.mbps(15), + maximumBitrate: cdk.Bitrate.mbps(15), routingScope: mediaconnect.RoutingScope.GLOBAL, tier: mediaconnect.RouterOutputTier.OUTPUT_50, configuration: mediaconnect.RouterOutputConfiguration.mediaLiveInputWithoutConnection({ @@ -1084,7 +1242,7 @@ declare const flow: mediaconnect.IFlow; const flowOutput = new mediaconnect.RouterOutput(stack, 'FlowOutput', { routerOutputName: 'flow-output', - maximumBitrate: mediaconnect.Bitrate.mbps(20), + maximumBitrate: cdk.Bitrate.mbps(20), routingScope: mediaconnect.RoutingScope.REGIONAL, tier: mediaconnect.RouterOutputTier.OUTPUT_100, configuration: mediaconnect.RouterOutputConfiguration.mediaConnectFlow({ @@ -1095,7 +1253,7 @@ const flowOutput = new mediaconnect.RouterOutput(stack, 'FlowOutput', { // MediaConnect Flow output without specific connection (requires explicit AZ) const flowOutputNoConnection = new mediaconnect.RouterOutput(stack, 'FlowOutputNoConnection', { routerOutputName: 'flow-output-no-connection', - maximumBitrate: mediaconnect.Bitrate.mbps(20), + maximumBitrate: cdk.Bitrate.mbps(20), routingScope: mediaconnect.RoutingScope.REGIONAL, tier: mediaconnect.RouterOutputTier.OUTPUT_100, configuration: mediaconnect.RouterOutputConfiguration.mediaConnectFlowWithoutConnection({ From eedf512c6f53666bb489842d53db49f90a9a6725 Mon Sep 17 00:00:00 2001 From: Jamie Mullan <46033424+jamiepmullan@users.noreply.github.com> Date: Thu, 16 Apr 2026 06:22:23 -0700 Subject: [PATCH 5/5] fixes pushed from review --- text/0884-aws-elemental-mediaconnect-l2.md | 193 ++++++++++++++++++--- 1 file changed, 165 insertions(+), 28 deletions(-) diff --git a/text/0884-aws-elemental-mediaconnect-l2.md b/text/0884-aws-elemental-mediaconnect-l2.md index 9b01ab3f2..9e26e536d 100644 --- a/text/0884-aws-elemental-mediaconnect-l2.md +++ b/text/0884-aws-elemental-mediaconnect-l2.md @@ -70,7 +70,7 @@ const bridge = new CfnBridge(stack, 'Bridge', { Same Configuration in an example L2: ```ts -const vpcInterface = mediaconnect.VpcInterface.create({ +const vpcInterface = mediaconnect.VpcInterface.define({ vpcInterfaceName: 'my-vpc-interface', role: role, securityGroups: [securityGroup], @@ -243,6 +243,7 @@ Property Interface for Flow: FlowProps { /** * Flow Name + * @default - auto-generated */ flowName?: string; @@ -401,15 +402,44 @@ flow.grants.actions(fn, 'mediaconnect:UpdateFlow'); ##### Flow Source Types -MediaConnect supports multiple source types for ingesting content into a flow: +MediaConnect supports multiple source types for ingesting content into a flow. The examples below use `NetworkConfiguration.publicNetwork()` for simplicity, but all protocol-based sources can also use `NetworkConfiguration.vpc()` with a VPC interface for private connectivity. -###### SRT Listener Source +###### RTP + +RTP (Real-time Transport Protocol) is a standard protocol for delivering audio and video over IP networks. + +```ts +new mediaconnect.Flow(stack, 'MyFlow', { + source: mediaconnect.SourceConfiguration.rtp({ + flowSourceName: 'rtp-source', + port: 5000, + network: mediaconnect.NetworkConfiguration.publicNetwork('203.0.113.0/24'), + }), +}); +``` + +###### RTP-FEC + +RTP with Forward Error Correction adds redundancy to recover lost packets without retransmission. Use this when contributing via RTP and you need packet recovery. + +```ts +new mediaconnect.Flow(stack, 'MyFlow', { + source: mediaconnect.SourceConfiguration.rtpFec({ + flowSourceName: 'rtp-fec-source', + port: 5000, + network: mediaconnect.NetworkConfiguration.publicNetwork('203.0.113.0/24'), + }), +}); +``` + +###### SRT Listener + +SRT (Secure Reliable Transport) in listener mode configures MediaConnect to listen on a specific port for incoming content. The upstream device connects to MediaConnect as a caller. ```ts new mediaconnect.Flow(stack, 'MyFlow', { source: mediaconnect.SourceConfiguration.srtListener({ - flowSourceName: 'live-encoder-source', - description: 'Live encoder feed', + flowSourceName: 'srt-listener-source', port: 5000, minLatency: Duration.millis(2000), network: mediaconnect.NetworkConfiguration.publicNetwork('203.0.113.0/24'), @@ -417,14 +447,71 @@ new mediaconnect.Flow(stack, 'MyFlow', { }); ``` +###### SRT Caller + +SRT in caller mode configures MediaConnect to connect to a remote SRT listener. Use this when the source device is listening for incoming connections rather than pushing content. + +```ts +new mediaconnect.Flow(stack, 'MyFlow', { + source: mediaconnect.SourceConfiguration.srtCaller({ + flowSourceName: 'srt-caller-source', + sourceListenerAddress: '198.51.100.10', + sourceListenerPort: 5000, + minLatency: Duration.millis(2000), + network: mediaconnect.NetworkConfiguration.publicNetwork('203.0.113.0/24'), + }), +}); +``` + +###### RIST + +RIST (Reliable Internet Stream Transport) provides reliable video transport with packet recovery. + +```ts +new mediaconnect.Flow(stack, 'MyFlow', { + source: mediaconnect.SourceConfiguration.rist({ + flowSourceName: 'rist-source', + port: 5000, + maxLatency: Duration.millis(2000), + network: mediaconnect.NetworkConfiguration.publicNetwork('203.0.113.0/24'), + }), +}); +``` + +###### Zixi Push + +Zixi Push uses the Zixi protocol for reliable video transport. Content is pushed to MediaConnect from a Zixi-compatible component upstream. + +```ts +new mediaconnect.Flow(stack, 'MyFlow', { + source: mediaconnect.SourceConfiguration.zixiPush({ + flowSourceName: 'zixi-source', + maxLatency: Duration.millis(2000), + network: mediaconnect.NetworkConfiguration.publicNetwork('203.0.113.0/24'), + }), +}); +``` + +###### Router Source + +Use a router source when the flow's source comes from a MediaConnect Router rather than a direct connection. + +```ts +new mediaconnect.Flow(stack, 'MyFlow', { + source: mediaconnect.SourceConfiguration.router(), +}); +``` + ###### VPC Source +Use a VPC-based source when you need a connection between a flow and your Amazon VPC. + ```ts declare const securityGroup: ec2.ISecurityGroup; declare const subnet: ec2.ISubnet; declare const role: iam.IRole; -const vpcInterface = mediaconnect.VpcInterface.create({ +const vpcInterface = mediaconnect.VpcInterface.define({ vpcInterfaceName: 'my-vpc-interface', role: role, securityGroups: [securityGroup], @@ -434,7 +521,6 @@ const vpcInterface = mediaconnect.VpcInterface.create({ new mediaconnect.Flow(stack, 'MyFlow', { source: mediaconnect.SourceConfiguration.rist({ flowSourceName: 'vpc-source', - description: 'VPC-based source', port: 5000, maxLatency: Duration.millis(2000), network: mediaconnect.NetworkConfiguration.vpc(vpcInterface), @@ -443,10 +529,11 @@ new mediaconnect.Flow(stack, 'MyFlow', { }); ``` -###### Entitled Source (From Another AWS Account) +###### Entitled Source + +Entitlements allow you to subscribe to content from another AWS account. ```ts -// Import an entitlement from another AWS account const entitlement = mediaconnect.FlowEntitlement.fromFlowEntitlementArn( stack, 'ImportedEntitlement', 'arn:aws:mediaconnect:us-west-2:111122223333:entitlement:1-11111111111111111111111111111111:MyEntitlement', @@ -459,6 +546,32 @@ new mediaconnect.Flow(stack, 'MyFlow', { }); ``` +###### Gateway Bridge Source + +Use a gateway bridge source when ingesting content from on-premises equipment through a MediaConnect gateway and bridge. + +```ts +declare const bridge: mediaconnect.IBridge; +declare const role: iam.IRole; +declare const securityGroup: ec2.ISecurityGroup; +declare const subnet: ec2.ISubnet; + +const vpcInterface = mediaconnect.VpcInterface.define({ + vpcInterfaceName: 'bridge-interface', + role: role, + securityGroups: [securityGroup], + subnet: subnet, +}); + +new mediaconnect.Flow(stack, 'MyFlow', { + source: mediaconnect.SourceConfiguration.gatewayBridge({ + bridge: bridge, + vpcInterface: vpcInterface, + }), + vpcInterfaces: [vpcInterface], +}); +``` + #### AWS Elemental MediaConnect Flow Source A FlowSource (`AWS::MediaConnect::FlowSource`) is a standalone resource used to add secondary @@ -534,14 +647,14 @@ For further information refer to [our documentation](https://docs.aws.amazon.com ##### Creating VPC Interfaces -VPC interfaces are created using the `VpcInterface.create()` factory method and can be reused across multiple flows and bridges: +VPC interfaces are created using the `VpcInterface.define()` factory method and can be reused across multiple flows and bridges: ```ts declare const role: iam.IRole; declare const securityGroup: ec2.ISecurityGroup; declare const subnet: ec2.ISubnet; -const vpcInterface = mediaconnect.VpcInterface.create({ +const vpcInterface = mediaconnect.VpcInterface.define({ vpcInterfaceName: 'my-vpc-interface', role: role, securityGroups: [securityGroup], @@ -559,7 +672,7 @@ declare const securityGroup: ec2.ISecurityGroup; declare const subnet: ec2.ISubnet; // ENA (Elastic Network Adapter) - Standard performance -const enaInterface = mediaconnect.VpcInterface.create({ +const enaInterface = mediaconnect.VpcInterface.define({ vpcInterfaceName: 'ena-interface', role: role, securityGroups: [securityGroup], @@ -568,7 +681,7 @@ const enaInterface = mediaconnect.VpcInterface.create({ }); // EFA (Elastic Fabric Adapter) - Required for CDI workflows -const efaInterface = mediaconnect.VpcInterface.create({ +const efaInterface = mediaconnect.VpcInterface.define({ vpcInterfaceName: 'efa-interface', role: role, securityGroups: [securityGroup], @@ -586,7 +699,7 @@ declare const role: iam.IRole; declare const securityGroup: ec2.ISecurityGroup; declare const subnet: ec2.ISubnet; -const vpcInterface = mediaconnect.VpcInterface.create({ +const vpcInterface = mediaconnect.VpcInterface.fromNetworkInterfaces({ vpcInterfaceName: 'existing-interface', role: role, securityGroups: [securityGroup], @@ -595,11 +708,6 @@ const vpcInterface = mediaconnect.VpcInterface.create({ }); ``` -**Note:** You cannot specify both `networkInterfaceType` and -`networkInterfaceIds`. Use `networkInterfaceType` to let MediaConnect -create interfaces automatically, or `networkInterfaceIds` to use -existing interfaces. - ##### Using VPC Interfaces with Flows VPC interfaces enable flows to use VPC-based sources and outputs: @@ -609,7 +717,7 @@ declare const role: iam.IRole; declare const securityGroup: ec2.ISecurityGroup; declare const subnet: ec2.ISubnet; -const vpcInterface = mediaconnect.VpcInterface.create({ +const vpcInterface = mediaconnect.VpcInterface.define({ vpcInterfaceName: 'flow-vpc-interface', role: role, securityGroups: [securityGroup], @@ -651,10 +759,13 @@ new mediaconnect.Bridge(stack, 'EgressBridge', { }); ``` -Property Interface for VpcInterface: +Property Interfaces for VpcInterface: ```ts -VpcInterfaceProps { +/** + * Props for VpcInterface.define() — creates new network interfaces + */ +VpcInterfaceDefineProps { /** * Unique name for the VPC interface */ @@ -676,16 +787,40 @@ VpcInterfaceProps { subnet: ISubnet; /** - * Pre-created network interface IDs - * @default - MediaConnect creates network interfaces automatically + * Network interface type (ENA or EFA) + * @default NetworkInterface.ENA */ - networkInterfaceIds?: string[]; + networkInterfaceType?: NetworkInterface; +} +/** + * Props for VpcInterface.fromNetworkInterfaces() — uses existing ENIs + */ +VpcInterfaceFromNetworkInterfacesProps { /** - * Network interface type (ENA or EFA) - * @default - Default network interface type + * Unique name for the VPC interface */ - networkInterfaceType?: NetworkInterface; + name: string; + + /** + * IAM role that MediaConnect assumes to create ENIs in your account + */ + role: IRole; + + /** + * Security groups to apply to the ENI + */ + securityGroups: ISecurityGroup[]; + + /** + * Subnet where the ENI will be created (must be in the same AZ as the flow) + */ + subnet: ISubnet; + + /** + * Pre-created network interface IDs + */ + networkInterfaceIds: string[]; } ``` @@ -802,6 +937,7 @@ Property Interface for Gateway: GatewayProps { /** * Gateway Name + * @default - auto-generated */ gatewayName?: string; @@ -853,6 +989,7 @@ Property Interface for Bridge: BridgeProps { /** * Bridge Name + * @default - auto-generated */ bridgeName?: string;