AWSTemplateFormatVersion: "2010-09-09"
Transform: AWS::Serverless-2016-10-31

Description:
  (v2.5.0) Integration service between AWS MediaServices and Cloud DRM KMS Service
  Copyright 2019-2026, Insys Video Technologies sp. z o.o. sp. k. All Rights Reserved.

Parameters:
  AuthenticationMethod:
    Type: String
    Description: Authentication method
    Default: OAUTH
    AllowedValues:
      - OAUTH
      - X-API-KEY
  ClientId:
    Type: String
    Default: ""
    Description: OAUTH Client Id (stored in the stack-managed Secrets Manager secret)
  ClientSecret:
    Type: String
    NoEcho: true
    Default: ""
    Description: OAUTH Client Secret (stored in the stack-managed Secrets Manager secret)
  TokenUrl:
    Type: String
    Default: ""
    Description: OAUTH Token url
  ApiKey:
    Type: String
    NoEcho: true
    Default: ""
    Description: X-API-KEY alternative authentication method (stored in the stack-managed Secrets Manager secret)
  SpekeUrl:
    Type: String
    Default: https://tenant-name.api.drm.cloud/kms/speke?tenantId=12345678-abcd-xxxx-xxxx-xxxxxxxxxxxx
    Description: Url to specify the SPEKE Live and VOD endpoint in Insys DRM Server that will provide keys for live or video on demand content
  GatewayType:
    Type: String
    Default: REGIONAL
    Description: >
      Choose a type of gateway. Private is available only in VPC, Regional available only in
      deployed region, Edge Optimized is deployed over the CloudFront network.
      Note: EDGE type depends on CloudFront which is not available in AWS European Sovereign Cloud;
      use REGIONAL or PRIVATE when AWSCloudType is EUSC.
    AllowedValues:
      - EDGE
      - REGIONAL
      - PRIVATE
  EnableGatewayLogging:
    Type: String
    Default: false
    AllowedValues: [ true, false ]
  LambdaLoggingLevel:
    Type: String
    Default: INFO
    AllowedValues:
      - DISABLED
      - INFO
      - DEBUG
  CreateMediaServiceRoles:
    Type: String
    Default: "true"
    AllowedValues: [ "true", "false" ]
    Description: >
      When 'true' (default) the stack creates IAM roles for MediaPackage / MediaConvert /
      MediaPackage VOD. When 'false' the stack does NOT create them and you supply your own,
      centrally-managed roles; those roles must have execute-api:Invoke permission on the API
      Gateway created by this stack (see Outputs: LiveSpekeInvokeUrl / VodSpekeInvokeUrl).
      Setting 'false' also allows the same stack name to be reused across regions (IAM role
      names are global to the account and would collide between regions when 'true').
  AWSCloudType:
    Type: String
    Default: GENERAL
    AllowedValues:
      - GENERAL
      - EUSC
    Description: >
      AWS Cloud type. Use 'GENERAL' for standard AWS commercial regions.
      Use 'EUSC' for AWS European Sovereign Cloud (Brandenburg, Germany).
      When set to 'EUSC', the EDGE gateway type is not available as it depends
      on CloudFront which is not available in AWS EUSC; REGIONAL is used instead.
  # SecurityPolicy_TLS13_1_3_2025_09 is the TLS 1.3-only policy (no TLS 1.2 fallback).
  # It is not used as the default here because MediaPackage VOD currently supports
  # at most TLS 1.2 when calling back into this gateway, so a TLS 1.3-only policy
  # would break VOD key requests. Switch to it once MediaPackage VOD supports TLS 1.3.
  ApiGatewaySecurityPolicy:
    Type: String
    Default: SecurityPolicy_TLS13_1_2_2021_06
    AllowedValues:
      - SecurityPolicy_TLS13_1_2_2021_06
      - SecurityPolicy_TLS13_1_3_2025_09
    Description: >
      The Transport Layer Security (TLS) security policy for the API Gateway RestApi.
      'SecurityPolicy_TLS13_1_2_2021_06' accepts both TLS 1.2 and TLS 1.3 (default).
      'SecurityPolicy_TLS13_1_3_2025_09' enforces TLS 1.3 only; do not use this until
      MediaPackage VOD supports TLS 1.3, as it currently supports at most TLS 1.2.

Rules:
  CheckEdgeGatewayTypeForEsc:
    RuleCondition: !Equals [!Ref AWSCloudType, EUSC]
    Assertions:
      - Assert: !Not
          - !Equals [!Ref GatewayType, EDGE]
        AssertDescription: >
          EDGE gateway type is not supported in AWS European Sovereign Cloud as it
          depends on CloudFront (not available in AWS EUSC). Use REGIONAL or PRIVATE instead.
  # The provided credentials seed the stack-managed Secrets Manager secret; validate them per auth method.
  CheckApiKeyWhenAuthMethodIsXApiKey:
    RuleCondition: !Equals [!Ref AuthenticationMethod, X-API-KEY]
    Assertions:
      - Assert: !Not
          - !Equals [!Ref ApiKey, ""]
        AssertDescription: 'ApiKey must not be empty for X-API-KEY AuthenticationMethod'
  CheckClientCredsWhenAuthMethodIsOAuth:
    RuleCondition: !Equals [!Ref AuthenticationMethod, OAUTH]
    Assertions:
      - Assert: !Not
          - !Equals [!Ref ClientId, ""]
        AssertDescription: 'ClientId must not be empty for OAUTH AuthenticationMethod'
      - Assert: !Not
          - !Equals [!Ref ClientSecret, ""]
        AssertDescription: 'ClientSecret must not be empty for OAUTH AuthenticationMethod'
      - Assert: !Not
          - !Equals [!Ref TokenUrl, ""]
        AssertDescription: 'TokenUrl must not be empty for OAUTH AuthenticationMethod'

Mappings:
  # Single source of truth for the deployed template version, surfaced at runtime (Lambda
  # env var -> outbound User-Agent) and as a stack Output. Bump in lockstep with the Description
  # above, spekegateway-v2/CHANGELOG.md and .version.yaml (the publish pipeline copies this file
  # verbatim and does not inject the version).
  TemplateMeta:
    Info:
      Version: "2.5.0"

Conditions:
  EnabledGatewayLogging: !Equals [true, !Ref EnableGatewayLogging]
  IsGeneralCloud: !Equals [!Ref AWSCloudType, GENERAL]
  # Media-service roles are created only in GENERAL cloud and only when CreateMediaServiceRoles is true.
  CreateGeneralMediaRoles: !And
    - !Equals [!Ref AWSCloudType, GENERAL]
    - !Equals [!Ref CreateMediaServiceRoles, "true"]

Resources:
  ## == Credentials secret (stack-managed) ==
  # The stack creates and owns the Secrets Manager secret; the Lambda always reads credentials
  # from it (never from environment variables). No explicit Name is set so deleting and recreating
  # a tenant stack does not collide with the Secrets Manager deletion recovery window.
  CredentialsSecret:
    Type: AWS::SecretsManager::Secret
    Properties:
      Description: !Sub "SpekeGateway credentials for ${AWS::StackName} (Cloud DRM api_key / client_id / client_secret)"
      SecretString: !Sub
        - '{"api_key":"${ApiKeyValue}","client_id":"${ClientIdValue}","client_secret":"${ClientSecretValue}"}'
        - ApiKeyValue: !Ref ApiKey
          ClientIdValue: !Ref ClientId
          ClientSecretValue: !Ref ClientSecret

  ## == Authorization lambda resource ==
  LambdaRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Sub "${AWS::StackName}-LambdaRole"
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - lambda.amazonaws.com
            Action:
              - sts:AssumeRole
      Policies:
        - PolicyName: !Sub "${AWS::StackName}-LambdaPolicy"
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Sid: Logging
                Effect: Allow
                Action:
                  - "logs:CreateLogGroup"
                  - "logs:CreateLogStream"
                  - "logs:PutLogEvents"
                Resource: "*"
              # Read the stack-managed credentials secret.
              - Sid: ReadCredentialSecret
                Effect: Allow
                Action: "secretsmanager:GetSecretValue"
                Resource: !Ref CredentialsSecret

  AuthorizationLambda:
    Type: AWS::Lambda::Function
    Properties:
      FunctionName: !Sub ${AWS::StackName}-Authorization
      Description: Authorization lambda for Live and VOD endpoint
      Role: !GetAtt LambdaRole.Arn
      Handler: index.lambda_handler
      Runtime: python3.12
      Code:
        ZipFile: |
          import os
          import re
          import json
          import urllib.parse
          import urllib.request
          import boto3

          # Header names whose values must never be written to logs in clear text. API Gateway
          # auth is X-Api-Key or OAuth bearer, and inbound requests are SigV4-signed
          # (Authorization + X-Amz-Security-Token), so these carry credentials.
          SENSITIVE_HEADERS = {"authorization", "x-api-key", "x-amz-security-token", "cookie", "set-cookie"}

          # Credentials are always read from the stack-managed Secrets Manager secret and cached
          # for the lifetime of a warm Lambda container (fetched once per container).
          _cred_cache = None

          def get_credentials():
            global _cred_cache
            if _cred_cache is None:
              raw = boto3.client("secretsmanager").get_secret_value(
                SecretId=os.getenv("credential_secret_arn"))["SecretString"]
              data = json.loads(raw)
              _cred_cache = {k: data.get(k) for k in ("api_key", "client_id", "client_secret")}
            return _cred_cache

          # Fully mask credential-bearing header values, e.g. "Authorization: ***", "X-Api-Key: ***".
          def mask_headers(headers):
            return {k: ("***" if k.lower() in SENSITIVE_HEADERS else v) for k, v in headers.items()}

          # Redact only the content-key bytes in CPIX responses (the PlainValue element);
          # the rest of the XML (KIDs, DRM systems, structure) is kept whole for troubleshooting.
          def redact_cpix(body):
            if not body:
              return body
            return re.sub(r"(<(?:\w+:)?PlainValue>).*?(</(?:\w+:)?PlainValue>)", r"\1***\2", body, flags=re.DOTALL)

          class AccessToken:
            def __init__(self, access_token, token_type, expires_in):
              self.access_token = access_token
              self.token_type = token_type
              self.expires_in = expires_in

          def get_tenant_id(speke_url):
            url = urllib.parse.urlparse(speke_url)
            query_params = urllib.parse.parse_qs(url.query)
            return query_params.get('tenantId', [None])[0]

          def get_token_cloud_oauth(speke_url, token_url, client_id, client_secret, logging_level):
            if(logging_level in ["INFO", "DEBUG"]):
              print("Getting token in oauth flow for", speke_url)

            request_data = {
              'grant_type': 'client_credentials',
              'client_id': client_id,
              'client_secret': client_secret
            }

            headers = {
              'Accept': 'application/json',
              'Content-Type': 'application/x-www-form-urlencoded'
            }

            encoded_data = urllib.parse.urlencode(request_data).encode('utf-8')

            req = urllib.request.Request(token_url, headers=headers, data=encoded_data, method='POST')
            try:
              with urllib.request.urlopen(req) as response:
                response_data = json.loads(response.read().decode('utf-8'))
                access_token = AccessToken(
                  response_data["access_token"],
                  response_data["token_type"],
                  response_data["expires_in"]
                )
                return access_token
            except urllib.error.HTTPError as e:
              print(f"Error getting OAuth token: HTTP error: {e.code}, {e.reason}")
              return None
            except urllib.error.URLError as e:
              print(f"Error getting OAuth token: URL error: {e}")
              return None
            finally:
              if(logging_level in ["INFO", "DEBUG"]):
                print("End getting token for", speke_url)

          def forward_original_request(input_data, url, token=None, api_key=None, logging_level=False):
            if(logging_level in ["INFO", "DEBUG"]):
              print("Start ForwardOriginalRequest")
              if(logging_level in ["DEBUG"]):
                print("ForwardOriginal INPUT", url)
                print("ForwardOriginal INPUT method=", input_data["httpMethod"])
                for header, value in mask_headers(input_data["headers"]).items():
                  print(f"ForwardOriginal INPUT header = {header} | {value}")

            headers = {
              **({'Authorization': f"Bearer {token.access_token}"} if token != None else {}),
              **({'X-Api-Key': api_key} if api_key != None else {}),
              "X-Tenant-Id": get_tenant_id(url)
            }
            if(logging_level in ["DEBUG"]):
              for header, value in mask_headers(headers).items():
                print(f"ForwardOriginal ADDED header = {header} | {value}")

            if input_data.get("headers"):
              for header, value in input_data["headers"].items():
                if header.lower() == "host":
                  headers["Host"] = urllib.parse.urlparse(url).netloc
                  if(logging_level in ["DEBUG"]):
                    print(f"ForwardOriginal CHANGED header = Host | {urllib.parse.urlparse(url).netloc}")
                elif header.lower() == "content-type" and value == "application/json":
                  headers["content-type"] = "application/xml"
                  if(logging_level in ["DEBUG"]):
                    print(f"ForwardOriginal CHANGED header = content-type | application/xml")
                else:
                  headers[header] = value

            # Stamp the gateway version so BBM can see the deployed version per tenant in the
            # DRM KMS access logs. Set after the copy loop so it wins over any inbound User-Agent.
            headers["User-Agent"] = f"SpekeGateway/{os.getenv('gateway_version', 'unknown')}"

            try:
              body = input_data["body"]
              if(logging_level in ["DEBUG"]):
                print(f"ForwardOriginal INPUT body = {body}")

              req = urllib.request.Request(url, headers=headers, data=body.encode('utf-8'), method=input_data["httpMethod"])
              with urllib.request.urlopen(req) as response:
                response_body = response.read().decode('utf-8')
                response_headers = {header: value for header, value in response.headers.items()}
                if(logging_level in ["DEBUG"]):
                  print("ForwardOriginalRequest url=", url)
                  # SPEKE/CPIX responses carry content keys - redact the key bytes before logging.
                  print("ForwardOriginalRequest response body=", redact_cpix(response_body))
                  for header, value in mask_headers(response_headers).items():
                    print(f"ForwardOriginalRequest response headers = {header} | {value}")
                if(logging_level in ["INFO", "DEBUG"]):
                  print("End ForwardOriginalRequest")
                return {
                  "statusCode": response.status,
                  "body": response_body,
                  "headers": response_headers
                }
            except urllib.error.HTTPError as e:
              print(f"Error invoking token: HTTP error: {e.code}, {e.reason}")
              return {
                "statusCode": 500
              }
            except urllib.error.URLError as e:
              print(f"Error invoking token: URL error: {e}")
              return {
                "statusCode": 500
              }

          def lambda_handler(event, context):
            logging_level = os.getenv("logging_level")
            if(logging_level in ["INFO", "DEBUG"]):
              print("Started authorization lambda")

            authentication_method = os.getenv("authentication_method")
            # Credentials come from Secrets Manager (when credential_secret_arn is set) or from env vars.
            creds = get_credentials()
            client_id = creds.get("client_id")
            client_secret = creds.get("client_secret")
            api_key = creds.get("api_key")
            speke_url = os.getenv("speke_url")
            token_url = os.getenv("token_url")

            if not speke_url:
              return {
                "statusCode": 400,
                "body": "Variable 'speke_url' missing",
                "headers": {"Content-Type": "text/plain"}
              }

            if authentication_method == "OAUTH":
              if not client_id:
                raise Exception("Variable 'client_id' missing")
              if not client_secret:
                raise Exception("Variable 'client_secret' missing")

              token = get_token_cloud_oauth(speke_url, token_url, client_id, client_secret, logging_level)
              if token:
                response = forward_original_request(event, url=speke_url, token=token, logging_level=logging_level)
                if(logging_level in ["INFO", "DEBUG"]):
                  print("Finished authorization lambda")
                return {
                  "statusCode": response["statusCode"],
                  "body": response["body"],
                  "headers": response["headers"]
                }
              else:
                if(logging_level in ["INFO", "DEBUG"]):
                  print("Finished authorization lambda")
                return {
                  "statusCode": 500,
                  "body": "Error getting OAuth token"
                }
            elif authentication_method == "X-API-KEY":
              if not api_key:
                raise Exception("Variable 'api_key' missing")

              response = forward_original_request(event, url=speke_url, api_key=api_key, logging_level=logging_level)

              if(logging_level in ["INFO", "DEBUG"]):
                print("Finished authorization lambda")

              return {
                "statusCode": response["statusCode"],
                "body": response["body"],
                "headers": response["headers"]
              }
            else:
              raise Exception("Unknown authentication method")
      Environment:
        Variables:
          authentication_method: !Ref AuthenticationMethod
          # Credentials are read at runtime from this secret - never placed in the environment.
          credential_secret_arn: !Ref CredentialsSecret
          speke_url:  !Ref SpekeUrl
          token_url:  !Ref TokenUrl
          logging_level: !Ref LambdaLoggingLevel
          gateway_version: !FindInMap [TemplateMeta, Info, Version]
      Timeout: 30
      MemorySize: 512

  InvokeAuthorizationLambda:
    Type: AWS::Lambda::Permission
    Properties:
      FunctionName: !GetAtt AuthorizationLambda.Arn
      Action: lambda:InvokeFunction
      Principal: apigateway.amazonaws.com
      # Scoped to the API Gateway created by this stack (previously no SourceArn/SourceAccount).
      SourceArn: !Sub "arn:${AWS::Partition}:execute-api:${AWS::Region}:${AWS::AccountId}:${ApiGateway}/*/*/*"

  AuthorizationLambdaLog:
    Type: AWS::Logs::LogGroup
    Properties:
      LogGroupName: !Join ["/", ["/aws/lambda", !Ref AuthorizationLambda]]
      RetentionInDays: 90

  ## == Api Gateway ==
  ApiGateway:
    Type: AWS::ApiGateway::RestApi
    Properties:
      Name: !Sub ${AWS::StackName}-GatewayApi
      # EndpointConfiguration.Types is the correct CFN property for endpoint type.
      # Parameters.endpointConfigurationTypes is silently ignored by the API Gateway service
      # and causes the default EDGE type to be used, which is not supported in AWS ESC.
      # When AWSCloudType=ESC, REGIONAL is enforced at the property level regardless of
      # the GatewayType parameter, so bypassing Rules validation cannot trigger an EDGE deployment.
      EndpointConfiguration:
        Types:
          - !If [IsGeneralCloud, !Ref GatewayType, REGIONAL]
      SecurityPolicy: !Ref ApiGatewaySecurityPolicy
      EndpointAccessMode: STRICT

  # VOD method
  ApiGatewayVodResource:
    Type: AWS::ApiGateway::Resource
    Properties:
      RestApiId: !Ref ApiGateway
      ParentId: !GetAtt ApiGateway.RootResourceId
      PathPart: "vod"

  ApiGatewayVodMethod:
    Type: AWS::ApiGateway::Method
    Properties:
      HttpMethod: POST
      AuthorizationType: AWS_IAM
      RestApiId: !Ref ApiGateway
      ResourceId: !Ref ApiGatewayVodResource
      Integration:
        Type: AWS_PROXY
        IntegrationHttpMethod: POST
        Uri: !Sub
          - arn:${AWS::Partition}:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${Arn}/invocations
          - Arn: !GetAtt AuthorizationLambda.Arn
      MethodResponses:
        - StatusCode: 200
        - StatusCode: 500

  # Live method
  ApiGatewayLiveResource:
    Type: AWS::ApiGateway::Resource
    Properties:
      RestApiId: !Ref ApiGateway
      ParentId: !GetAtt ApiGateway.RootResourceId
      PathPart: "live"

  ApiGatewayLiveMethod:
    Type: AWS::ApiGateway::Method
    Properties:
      HttpMethod: POST
      AuthorizationType: AWS_IAM
      RestApiId: !Ref ApiGateway
      ResourceId: !Ref ApiGatewayLiveResource
      Integration:
        Type: AWS_PROXY
        IntegrationHttpMethod: POST
        Uri: !Sub
          - arn:${AWS::Partition}:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${Arn}/invocations
          - Arn: !GetAtt AuthorizationLambda.Arn
      MethodResponses:
        - StatusCode: 200
        - StatusCode: 500

  ApiGatewayDeploymentVer2:
    Type: AWS::ApiGateway::Deployment
    DependsOn:
      - ApiGatewayVodMethod
      - ApiGatewayLiveMethod
    Properties:
      RestApiId: !Ref ApiGateway
      StageName: v1

  ApiGatewayStage:
    Type: AWS::ApiGateway::Stage
    Properties:
      DeploymentId: !Ref ApiGatewayDeploymentVer2
      MethodSettings:
        # Do not log full request/response payloads (CPIX with content keys) to CloudWatch.
        - DataTraceEnabled: false
          HttpMethod: "*"
          LoggingLevel: !If [ EnabledGatewayLogging, INFO, !Ref "AWS::NoValue" ]
          ResourcePath: "/*"
      RestApiId: !Ref ApiGateway
      StageName: LATEST

  ApiGatewayLatestStageLog:
    Type: AWS::Logs::LogGroup
    Condition: EnabledGatewayLogging
    DependsOn:
      - AuthorizationLambda
    Properties:
      LogGroupName: !Sub "${AWS::StackName}/apigateway/${ApiGateway}/LATEST"
      RetentionInDays: 90

  ## == IAM Roles ==
  MediaPackageRole:
    Condition: CreateGeneralMediaRoles
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Sub "${AWS::StackName}-MediaPackageRole"
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - mediapackage.amazonaws.com
                - mediapackagev2.amazonaws.com
            Action:
              - sts:AssumeRole
      Policies:
        - PolicyName: !Sub "${AWS::StackName}-invokeLiveSpekeEndpointPolicy"
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Sid: invokeSpekePolicy
                Effect: Allow
                Action: "execute-api:Invoke"
                Resource: !Sub "arn:${AWS::Partition}:execute-api:${AWS::Region}:${AWS::AccountId}:${ApiGateway}/*/POST/live"

  MediaConvertRole:
    Condition: CreateGeneralMediaRoles
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Sub "${AWS::StackName}-MediaConvertRole"
      Description: "ARN of IAM Role for MediaConvert with access to SpekeGateway's VOD endpoint and full access to S3"
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service:
                -  mediaconvert.amazonaws.com
            Action:
              - sts:AssumeRole
      ManagedPolicyArns:
        - !Sub "arn:${AWS::Partition}:iam::${AWS::Partition}:policy/AmazonS3FullAccess"
      Policies:
        - PolicyName: !Sub "invokeVodSpekeEndpointPolicy"
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Sid: invokeSpekePolicy
                Effect: Allow
                Action: "execute-api:Invoke"
                Resource: !Sub "arn:${AWS::Partition}:execute-api:${AWS::Region}:${AWS::AccountId}:${ApiGateway}/*/POST/vod"

  MediaPackageVodRole:
    Condition: CreateGeneralMediaRoles
    Type: AWS::IAM::Role
    Properties:
      Description: "ARN of IAM Role for MediaPackage VOD with access to SpekeGateway's VOD endpoint"
      RoleName: !Sub "${AWS::StackName}-MediaPackageVodRole"
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service:
                -  mediapackage.amazonaws.com
            Action:
              - sts:AssumeRole
      Policies:
        - PolicyName: !Sub "invokeVodSpekeEndpointPolicy"
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Sid: invokeSpekePolicy
                Effect: Allow
                Action: "execute-api:Invoke"
                Resource: !Sub "arn:${AWS::Partition}:execute-api:${AWS::Region}:${AWS::AccountId}:${ApiGateway}/*/POST/vod"

Outputs:
  TemplateVersion:
    Description: "Deployed SpekeGateway CloudFormation template version"
    Value: !FindInMap [TemplateMeta, Info, Version]
    Export:
      Name: !Sub "${AWS::StackName}:TemplateVersion"
  LiveSpekeInvokeUrl:
    Description: "SpekeGateway endpoint uses for Live content"
    Value: !Sub "https://${ApiGateway}.execute-api.${AWS::Region}.amazonaws.com/LATEST/live"
    Export:
      Name: !Sub "${AWS::StackName}:LiveSpekeInvokeUrl"
  VodSpekeInvokeUrl:
    Description: "SpekeGateway endpoint uses for VOD content"
    Value: !Sub "https://${ApiGateway}.execute-api.${AWS::Region}.amazonaws.com/LATEST/vod"
    Export:
      Name: !Sub "${AWS::StackName}:VodSpekeInvokeUrl"
  MediaPackageRole:
    Condition: CreateGeneralMediaRoles
    Description: "ARN of IAM Role for MediaPackage Live with access to SpekeGateway's Live endpoint"
    Value: !GetAtt MediaPackageRole.Arn
    Export:
      Name: !Sub "${AWS::StackName}:MediaPackageRole"
  MediaConvertRole:
    Condition: CreateGeneralMediaRoles
    Description: "ARN of IAM Role for MediaConvert with access to SpekeGateway VOD endpoint and full access to S3"
    Value: !GetAtt MediaConvertRole.Arn
    Export:
      Name: !Sub "${AWS::StackName}:MediaConvertRole"
  MediaPackageVodRole:
    Condition: CreateGeneralMediaRoles
    Description: "ARN of IAM Role for MediaPackage VOD with access to SpekeGateway's VOD endpoint"
    Value: !GetAtt MediaPackageVodRole.Arn
    Export:
      Name: !Sub "${AWS::StackName}:MediaPackageVodRole"

Metadata:
  AWS::CloudFormation::Interface:
    ParameterGroups:
      - Label:
          default: 'Authentication config'
        Parameters:
          - AuthenticationMethod
          - ClientId
          - ClientSecret
          - TokenUrl
          - ApiKey
      - Label:
          default: 'Cloud Video Kit configuration'
        Parameters:
          - SpekeUrl
      - Label:
          default: 'Technical parameters'
        Parameters:
          - ApiGatewaySecurityPolicy
          - AWSCloudType
          - GatewayType
          - CreateMediaServiceRoles
          - EnableGatewayLogging
          - LambdaLoggingLevel
