AWSTemplateFormatVersion: "2010-09-09"

Description: >
  This AWS CloudFormation template deploys an AWS Private CA with a multi-level, 
  subordinate hierarchy in a specified AWS Region and account. It supports conditionally
  specifying the level of subordination. For example, create a root and one subordinate
  CA (two CA level hierarchy) by leaving the parameters of subordinates 2-4 blank.
  Optionally, create S3 buckets for the Certificate Revocation List (CRL) and for CRL
  access logging and optionally share the end-entity certificate generating subordinate
  to an AWS organization or organizational unit via AWS Resource Access Manager (AWS RAM).

# See also https://aws.amazon.com/blogs/security/how-to-use-aws-ram-to-share-your-acm-private-ca-cross-account/
#          https://docs.aws.amazon.com/privateca/latest/userguide/ca-hierarchy.html
#          https://docs.aws.amazon.com/privateca/latest/userguide/create-CA.html

Parameters:
  # Certificate Subject Info
  pOrganization:
    Description: The name of the organization for the certificate subject, e.g. MyDomain LLC. Max length of 64 characters.
    Type: String
    MaxLength: 64
  pOrganizationalUnit:
    Description: The name of the organizational unit that owns the certificate for the certificate subject, e.g. Cloud IT. Max length of 64 characters.
    Type: String
    MaxLength: 64
  pCountry:
    Description: The two character country code for the certificate subject, e.g. US.
    Type: String
    Default: US
    MinLength: 2
    MaxLength: 2
    AllowedPattern: "[A-Z]*"
  pState:
    Description: The state for the certificate subject, e.g. California. Max length of 128 characters.
    Type: String
    MaxLength: 128
  pLocality:
    Description: The locality for the certificate subject, e.g. Los Angeles. Max length 128 characters.
    Type: String
    MaxLength: 128
  # Certificate Encryption Info
  pKeyAlgorithm:
    Description: Type of the public key algorithm and size, in bits, of the key pair that your CA creates when it issues a certificate.
    Type: String
    Default: RSA_2048
    AllowedValues:
      - RSA_2048
      - RSA_4096
      - EC_prime256v1
      - EC_secp384r1
  pSigningAlgorithm:
    Description: Name of the algorithm your private CA uses to sign certificate requests.
    Type: String
    Default: SHA256WITHRSA
    AllowedValues:
      - SHA256WITHECDSA
      - SHA256WITHRSA
      - SHA384WITHECDSA
      - SHA384WITHRSA
      - SHA512WITHECDSA
      - SHA512WITHRSA
  # Root Certificate Authority Info
  pRootCommonName:
    Description: Fully qualified domain name (FQDN) associated with the certificate subject, e.g. mydomain.io. Max length of 64 characters.
    Type: String
    MaxLength: 64
  pRootCACertExpiry:
    Description: Certificate expiration in days, e.g. 3651.
    Type: String
    Default: 3651
  pRootCRLExpiry:
    Description: Root CRL expiry in days, must be higher than the subordinate CRL expiry, e.g. 90.
    Type: String
    Default: 90
  # 1st Level Subordinate Certificate Authority Info
  pSub1CommonName:
    Description: Fully qualified domain name (FQDN) associated with the certificate subject, e.g. subdomain1.mydomain.io. Leave this blank to skip building this subordinate CA, not best practice to have no subordinates. Max length of 64 characters.
    Type: String
    MaxLength: 64
  pSub1CACertExpiry:
    Description: Certificate expiration in days, e.g. 1825. Must be smaller than higher level CAs.
    Type: String
    Default: 1825
  pSub1CRLExpiry:
    Description: 1st Subordinate CRL expiry in days, must be smaller than higher level CAs, e.g. 60.
    Type: String
    Default: 60
  # 2nd Level Subordinate Certificate Authority Info
  pSub2CommonName:
    Description: Fully qualified domain name (FQDN) associated with the certificate subject, e.g. subdomain2.subdomain1.mydomain.io. Leave this blank to skip building this subordinate CA. Max length of 64 characters.
    Type: String
    MaxLength: 64
  pSub2CACertExpiry:
    Description: Certificate expiration in days, e.g. 1095. Must be smaller than higher level CAs.
    Type: String
    Default: 1095
  pSub2CRLExpiry:
    Description: 2nd Subordinate CRL expiry in days, must be smaller than higher level CAs, e.g. 50.
    Type: String
    Default: 50
  # 3rd Level Subordinate Certificate Authority Info
  pSub3CommonName:
    Description: Fully qualified domain name (FQDN) associated with the certificate subject, e.g. subdomain3.subdomain2.subdomain1.mydomain.io. Leave this blank to skip building this subordinate CA. Max length of 64 characters.
    Type: String
    MaxLength: 64
  pSub3CACertExpiry:
    Description: Certificate expiration in days, e.g. 730. Must be smaller than higher level CAs.
    Type: String
    Default: 730
  pSub3CRLExpiry:
    Description: 1st Subordinate CRL expiry in days, must be smaller than higher level CAs, e.g. 40.
    Type: String
    Default: 40
  # 4th Level Subordinate Certificate Authority Info
  pSub4CommonName:
    Description: Fully qualified domain name (FQDN) associated with the certificate subject, e.g. subdomain4.subdomain3.subdomain2.subdomain1.mydomain.io. Leave this blank to skip building this subordinate CA. Max length of 64 characters.
    Type: String
    MaxLength: 64
  pSub4CACertExpiry:
    Description: Certificate expiration in days, e.g. 365. Must be smaller than higher level CAs.
    Type: String
    Default: 365
  pSub4CRLExpiry:
    Description: 1st Subordinate CRL expiry in days, must be smaller than higher level CAs, e.g. 30.
    Type: String
    Default: 30
  # Optional buckets
  pRootCRLBucket:
    Description: Name of S3 bucket for the CRL, e.g. mydomain-io-awspca-crl. Leaving this blank will disable CRL.
    Type: String
  pLogCRLBucket:
    Description: Name of S3 bucket where the log of CRL access is stored, e.g. mydomain-io-awspca-log. Leaving this blank will disable CRL access logging.
    Type: String
  # AWS Organization Account Info
  pManagmentAccount:
    Description: Share the lowest level CA to the specified AWS Organizations management account, e.g., 112233445566. Leaving this blank will not create the AWS RAM share.
    Type: String
  pAWSOrganizationId:
    Description: Share the lowest level CA to the specified AWS Organization e.g., o-fortestorg. Required with account ID above for the AWS RAM share. Include an OU below to share at the OU level.
    Type: String
    Default: ""
  pAWSOrganizationalUnit:
    Description: Share the lowest level CA to the specified AWS Organizational Unit e.g. ou-foratestou. Leave this blank and include an Org ID above to share at the Organization level.
    Type: String
    Default: ""

Metadata:
  AWS::CloudFormation::Interface:
    ParameterGroups:
      - Label:
          default: "Certificate Subject Info"
        Parameters:
          - pOrganization
          - pOrganizationalUnit
          - pCountry
          - pState
          - pLocality
      - Label:
          default: "Certificate Encryption Info"
        Parameters:
          - pKeyAlgorithm
          - pSigningAlgorithm
      - Label:
          default: "Root Certificate Authority Info"
        Parameters:
          - pRootCommonName
          - pRootCACertExpiry
          - pRootCRLBucket
          - pRootCRLExpiry
          - pLogCRLBucket
      - Label:
          default: "1st Level Subordinate Certificate Authority Info"
        Parameters:
          - pSub1CommonName
          - pSub1CACertExpiry
          - pSub1CRLExpiry
      - Label:
          default: "2nd Level Subordinate Certificate Authority Info"
        Parameters:
          - pSub2CommonName
          - pSub2CACertExpiry
          - pSub2CRLExpiry
      - Label:
          default: "3rd Level Subordinate Certificate Authority Info"
        Parameters:
          - pSub3CommonName
          - pSub3CACertExpiry
          - pSub3CRLExpiry
      - Label:
          default: "4th Level Subordinate Certificate Authority Info"
        Parameters:
          - pSub4CommonName
          - pSub4CACertExpiry
          - pSub4CRLExpiry
      - Label:
          default: "AWS Organization Account Info"
        Parameters:
          - pManagmentAccount
          - pAWSOrganizationId
          - pAWSOrganizationalUnit
    #Define user friendly names for the parameters
    ParameterLabels:
      pOrganization:
        default: Organization
      pOrganizationalUnit:
        default: Organizational Unit
      pCountry:
        default: Country
      pState:
        default: State
      pLocality:
        default: Locality
      pKeyAlgorithm:
        default: Key Algorithm
      pSigningAlgorithm:
        default: SigningAlgorithm
      pRootCommonName:
        default: Common Name
      pRootCACertExpiry:
        default: Certificate Expiry
      pRootCRLExpiry:
        default: CRL Expiry
      pSub1CommonName:
        default: Common Name
      pSub1CACertExpiry:
        default: Certificate Expiry
      pSub1CRLExpiry:
        default: CRL Expiry
      pSub2CommonName:
        default: Common Name
      pSub2CACertExpiry:
        default: Certificate Expiry
      pSub2CRLExpiry:
        default: CRL Expiry
      pSub3CommonName:
        default: Common Name
      pSub3CACertExpiry:
        default: Certificate Expiry
      pSub3CRLExpiry:
        default: CRL Expiry
      pSub4CommonName:
        default: Common Name
      pSub4CACertExpiry:
        default: Certificate Expiry
      pSub4CRLExpiry:
        default: CRL Expiry
      pRootCRLBucket:
        default: CRL Bucket Name
      pLogCRLBucket:
        default: Log Bucket Name
      pManagmentAccount:
        default: AWS Organizations Management Account
      pAWSOrganizationId:
        default: AWS Organization Id
      pAWSOrganizationalUnit:
        default: AWS Organizational Unit

Conditions:
  # Do not create subordinates if their certificate subject common names are left blank
  #   If Sub1 is not built, don't build any others. These subordinates must be built in order
  EnableCRL: !Not
    - !Equals
      - !Ref pRootCRLBucket
      - ""
  EnableCRLLog: !And
    - !Condition EnableCRL
    - !Not
      - !Equals
        - !Ref pLogCRLBucket
        - ""
  CreateSub1: !Not
    - !Equals
      - !Ref pSub1CommonName
      - ""
  CreateSub2: !And
    - !Condition CreateSub1
    - !Not
      - !Equals
        - !Ref pSub2CommonName
        - ""
  CreateSub3: !And
    - !Condition CreateSub2
    - !Not
      - !Equals
        - !Ref pSub3CommonName
        - ""
  CreateSub4: !And
    - !Condition CreateSub3
    - !Not
      - !Equals
        - !Ref pSub4CommonName
        - ""
  CreateOrgShare: !And
    - !Not
      - !Equals
        - !Ref pManagmentAccount
        - ""
    - !Not
      - !Equals
        - !Ref pAWSOrganizationId
        - ""
  CreateOUShare: !And
    - !Condition CreateOrgShare
    - !Not
      - !Equals
        - !Ref pAWSOrganizationalUnit
        - ""
  CreateShare: !Or
      - !Condition CreateOrgShare
      - !Condition CreateOUShare
  CreateRootShare: !And
    - !Condition CreateShare
    - !Not
      - !Condition CreateSub1
  CreateSub1Share: !And
    - !Condition CreateShare
    - !And
      - !Condition CreateSub1
      - !Not
        - !Condition CreateSub2
  CreateSub2Share: !And
    - !Condition CreateShare
    - !And
      - !Condition CreateSub2
      - !Not
        - !Condition CreateSub3
  CreateSub3Share: !And
    - !Condition CreateShare
    - !And
      - !Condition CreateSub3
      - !Not
        - !Condition CreateSub4
  CreateSub4Share: !And
    - !Condition CreateShare
    - !Condition CreateSub4

Resources:
  rLogCRLBucket:
  # checkov:skip=CKV_AWS_18: The solution does not require access logging for this bucket.
  # checkov:skip=CKV_AWS_21: The solution does not require versioning on S3
    Type: AWS::S3::Bucket
    Metadata:
      cfn_nag:
        rules_to_suppress:
          - id: W51
            reason: "This is logging bucket, AccessControl = LogDeliveryWrite"
          - id: W35
            reason: "The solution does not require access logging for this bucket"
      cfn-lint:
        config:
          ignore_checks:
            - W3011
    Condition: EnableCRLLog
    DeletionPolicy: Retain
    Properties:
      BucketName: !Ref pLogCRLBucket
      AccessControl: LogDeliveryWrite
      BucketEncryption: #SSE-S3 vs SSE-KMS, AWS vs Customer KMS
        ServerSideEncryptionConfiguration:
          - ServerSideEncryptionByDefault:
              SSEAlgorithm: AES256
      #LifecycleConfiguration:
      Tags: 
        - Key: "Purpose"
          Value: "AWSPCA CRL Access Logs"
      PublicAccessBlockConfiguration:
        RestrictPublicBuckets: True
        BlockPublicAcls: True
        BlockPublicPolicy: True
        IgnorePublicAcls: True
  rRootCRLBucket:
  # checkov:skip=CKV_AWS_21: The solution does not require versioning on S3
    Type: AWS::S3::Bucket
    Metadata:
      cfn-lint:
        config:
          ignore_checks:
            - W3011
    Condition: EnableCRL
    DeletionPolicy: Retain
    Properties:
      BucketName: !Ref pRootCRLBucket
      AccessControl: Private
      OwnershipControls:
        Rules:
          - ObjectOwnership: BucketOwnerPreferred
      LoggingConfiguration: !If
        - EnableCRLLog
        - DestinationBucketName: !Ref rLogCRLBucket
          LogFilePrefix: RootCA
        - !Ref "AWS::NoValue"
      BucketEncryption: #SSE-S3 vs SSE-KMS, AWS vs Customer KMS
        ServerSideEncryptionConfiguration:
          - ServerSideEncryptionByDefault:
              SSEAlgorithm: AES256
      Tags: 
        - Key: "Purpose"
          Value: "AWSPCA Certificate Revocation List"
      PublicAccessBlockConfiguration:
        RestrictPublicBuckets: True
        BlockPublicAcls: True
        BlockPublicPolicy: True
        IgnorePublicAcls: True
  rRootCRLBucketPolicy:
    Type: AWS::S3::BucketPolicy
    Condition: EnableCRL
    Properties:
      Bucket: !Ref rRootCRLBucket
      PolicyDocument:
        Statement:
          - Sid: AllowAWSPCAPutObjectAccessForCRLAndChainCertificates
            Action:
              - "s3:GetBucketAcl"
              - "s3:GetBucketLocation"
              - "s3:PutObject"
              - "s3:PutObjectAcl"
            Effect: "Allow"
            Resource:
              - !Join ["", ["arn:aws:s3:::", !Ref pRootCRLBucket, "/*"]]
              - !Join ["", ["arn:aws:s3:::", !Ref pRootCRLBucket]]
            Principal:
              Service:
                - "acm-pca.amazonaws.com"
  #Bucket Policy has to be created before using it in RootCA
  BucketPolicyWaitHandle: 
    Condition: EnableCRL
    DependsOn: rRootCRLBucketPolicy
    Type: "AWS::CloudFormation::WaitConditionHandle"
  BlankWaitHandle: 
    Type: "AWS::CloudFormation::WaitConditionHandle"
  BucketPolicyWaitCondition: 
    Type: "AWS::CloudFormation::WaitCondition"
    Properties: 
      Handle: !If [EnableCRL, !Ref BucketPolicyWaitHandle, !Ref BlankWaitHandle]
      Timeout: "5" 
      Count: 0
  rRootCA:
    Type: AWS::ACMPCA::CertificateAuthority
    DependsOn: BucketPolicyWaitCondition 
    Properties:
      Type: ROOT
      Subject:
        CommonName: !Ref pRootCommonName
        Organization: !Ref pOrganization
        OrganizationalUnit: !Ref pOrganizationalUnit
        Country: !Ref pCountry
        State: !Ref pState
        Locality: !Ref pLocality
      KeyAlgorithm: !Ref pKeyAlgorithm
      SigningAlgorithm: !Ref pSigningAlgorithm
      RevocationConfiguration:
        CrlConfiguration: !If
          - EnableCRL
          - Enabled: true
            ExpirationInDays: !Ref pRootCRLExpiry
            S3BucketName: !Ref rRootCRLBucket
            S3ObjectAcl: "BUCKET_OWNER_FULL_CONTROL"
          - Enabled: false
      Tags: 
        - Key: "Purpose"
          Value: "Root CA"
  rRootCACert:
    Type: "AWS::ACMPCA::Certificate"
    Properties:
      CertificateAuthorityArn: !Ref rRootCA
      CertificateSigningRequest: !GetAtt rRootCA.CertificateSigningRequest
      SigningAlgorithm: !Ref pSigningAlgorithm
      TemplateArn: "arn:aws:acm-pca:::template/RootCACertificate/V1"
      Validity:
        Type: DAYS
        Value: !Ref pRootCACertExpiry
  rRootCAActivation:
    Type: "AWS::ACMPCA::CertificateAuthorityActivation"
    Properties:
      CertificateAuthorityArn: !Ref rRootCA
      Certificate: !GetAtt rRootCACert.Certificate
      Status: ACTIVE
  rSubordinateCA1:
    Type: AWS::ACMPCA::CertificateAuthority
    Condition: CreateSub1
    DependsOn: rRootCA
    Properties:
      Type: SUBORDINATE
      Subject:
        CommonName: !Ref pSub1CommonName
        Organization: !Ref pOrganization
        OrganizationalUnit: !Ref pOrganizationalUnit
        Country: !Ref pCountry
        State: !Ref pState
        Locality: !Ref pLocality
      KeyAlgorithm: !Ref pKeyAlgorithm
      SigningAlgorithm: !Ref pSigningAlgorithm
      RevocationConfiguration:
        CrlConfiguration: !If
          - EnableCRL
          - Enabled: true
            ExpirationInDays: !Ref pSub1CRLExpiry
            S3BucketName: !Ref rRootCRLBucket
            S3ObjectAcl: "BUCKET_OWNER_FULL_CONTROL"
          - Enabled: false
      Tags: 
        - Key: "Purpose"
          Value: "Subordinate CA1"
  rSubordinateCA1Cert:
    Type: "AWS::ACMPCA::Certificate"
    Condition: CreateSub1
    DependsOn: rRootCAActivation
    Properties:
      CertificateAuthorityArn: !Ref rRootCA
      CertificateSigningRequest: !GetAtt rSubordinateCA1.CertificateSigningRequest
      SigningAlgorithm: !Ref pSigningAlgorithm
      TemplateArn: !If
        - CreateSub4
        - "arn:aws:acm-pca:::template/SubordinateCACertificate_PathLen3/V1"
        - !If
          - CreateSub3
          - "arn:aws:acm-pca:::template/SubordinateCACertificate_PathLen2/V1"
          - !If
            - CreateSub2
            - "arn:aws:acm-pca:::template/SubordinateCACertificate_PathLen1/V1"
            - !If
              - CreateSub1
              - "arn:aws:acm-pca:::template/SubordinateCACertificate_PathLen0/V1"
              - !Ref "AWS::NoValue"

      Validity:
        Type: DAYS
        Value: !Ref pSub1CACertExpiry
  rSubordinateCA1Activation:
    Type: "AWS::ACMPCA::CertificateAuthorityActivation"
    Condition: CreateSub1
    Properties:
      CertificateAuthorityArn: !Ref rSubordinateCA1
      Certificate: !GetAtt rSubordinateCA1Cert.Certificate
      CertificateChain: !GetAtt rRootCAActivation.CompleteCertificateChain
      Status: ACTIVE
  rSubordinateCA2:
    Type: AWS::ACMPCA::CertificateAuthority
    Condition: CreateSub2
    DependsOn: rSubordinateCA1
    Properties:
      Type: SUBORDINATE
      Subject:
        CommonName: !Ref pSub2CommonName
        Organization: !Ref pOrganization
        OrganizationalUnit: !Ref pOrganizationalUnit
        Country: !Ref pCountry
        State: !Ref pState
        Locality: !Ref pLocality
      KeyAlgorithm: !Ref pKeyAlgorithm
      SigningAlgorithm: !Ref pSigningAlgorithm
      RevocationConfiguration:
        CrlConfiguration: !If
          - EnableCRL
          - Enabled: true
            ExpirationInDays: !Ref pSub2CRLExpiry
            S3BucketName: !Ref rRootCRLBucket
            S3ObjectAcl: "BUCKET_OWNER_FULL_CONTROL"
          - Enabled: false
      Tags: 
        - Key: "Purpose"
          Value: "Subordinate CA2"
  rSubordinateCA2Cert:
    Type: "AWS::ACMPCA::Certificate"
    Metadata:
      cfn-lint:
        config:
          ignore_checks:
            - W3005
    Condition: CreateSub2
    DependsOn: rSubordinateCA1Activation
    Properties:
      CertificateAuthorityArn: !Ref rSubordinateCA1Activation
      CertificateSigningRequest: !GetAtt rSubordinateCA2.CertificateSigningRequest
      SigningAlgorithm: !Ref pSigningAlgorithm
      TemplateArn: !If
        - CreateSub4
        - "arn:aws:acm-pca:::template/SubordinateCACertificate_PathLen2/V1"
        - !If
          - CreateSub3
          - "arn:aws:acm-pca:::template/SubordinateCACertificate_PathLen1/V1"
          - !If
            - CreateSub2
            - "arn:aws:acm-pca:::template/SubordinateCACertificate_PathLen0/V1"
            - !Ref "AWS::NoValue"
      Validity:
        Type: DAYS
        Value: !Ref pSub2CACertExpiry
  rSubordinateCA2Activation:
    Type: "AWS::ACMPCA::CertificateAuthorityActivation"
    Condition: CreateSub2
    Properties:
      CertificateAuthorityArn: !Ref rSubordinateCA2
      Certificate: !GetAtt rSubordinateCA2Cert.Certificate
      CertificateChain: !GetAtt rSubordinateCA1Activation.CompleteCertificateChain
      Status: ACTIVE
  rSubordinateCA3:
    Type: AWS::ACMPCA::CertificateAuthority
    Condition: CreateSub3
    DependsOn: rSubordinateCA2
    Properties:
      Type: SUBORDINATE
      Subject:
        CommonName: !Ref pSub3CommonName
        Organization: !Ref pOrganization
        OrganizationalUnit: !Ref pOrganizationalUnit
        Country: !Ref pCountry
        State: !Ref pState
        Locality: !Ref pLocality
      KeyAlgorithm: !Ref pKeyAlgorithm
      SigningAlgorithm: !Ref pSigningAlgorithm
      RevocationConfiguration:
        CrlConfiguration: !If
          - EnableCRL
          - Enabled: true
            ExpirationInDays: !Ref pSub3CRLExpiry
            S3BucketName: !Ref rRootCRLBucket
            S3ObjectAcl: "BUCKET_OWNER_FULL_CONTROL"
          - Enabled: false
      Tags: 
        - Key: "Purpose"
          Value: "Subordinate CA3"
  rSubordinateCA3Cert:
    Type: "AWS::ACMPCA::Certificate"
    Metadata:
      cfn-lint:
        config:
          ignore_checks:
            - W3005
    Condition: CreateSub3
    DependsOn: rSubordinateCA2Activation
    Properties:
      CertificateAuthorityArn: !Ref rSubordinateCA2Activation
      CertificateSigningRequest: !GetAtt rSubordinateCA3.CertificateSigningRequest
      SigningAlgorithm: !Ref pSigningAlgorithm
      TemplateArn: !If
        - CreateSub4
        - "arn:aws:acm-pca:::template/SubordinateCACertificate_PathLen1/V1"
        - !If
          - CreateSub3
          - "arn:aws:acm-pca:::template/SubordinateCACertificate_PathLen0/V1"
          - !Ref "AWS::NoValue"
      Validity:
        Type: DAYS
        Value: !Ref pSub3CACertExpiry
  rSubordinateCA3Activation:
    Type: "AWS::ACMPCA::CertificateAuthorityActivation"
    Condition: CreateSub3
    Properties:
      CertificateAuthorityArn: !Ref rSubordinateCA3
      Certificate: !GetAtt rSubordinateCA3Cert.Certificate
      CertificateChain: !GetAtt rSubordinateCA2Activation.CompleteCertificateChain
      Status: ACTIVE
  rSubordinateCA4:
    Type: AWS::ACMPCA::CertificateAuthority
    Condition: CreateSub4
    DependsOn: rSubordinateCA3
    Properties:
      Type: SUBORDINATE
      Subject:
        CommonName: !Ref pSub4CommonName
        Organization: !Ref pOrganization
        OrganizationalUnit: !Ref pOrganizationalUnit
        Country: !Ref pCountry
        State: !Ref pState
        Locality: !Ref pLocality
      KeyAlgorithm: !Ref pKeyAlgorithm
      SigningAlgorithm: !Ref pSigningAlgorithm
      RevocationConfiguration:
        CrlConfiguration: !If
          - EnableCRL
          - Enabled: true
            ExpirationInDays: !Ref pSub4CRLExpiry
            S3BucketName: !Ref rRootCRLBucket
            S3ObjectAcl: "BUCKET_OWNER_FULL_CONTROL"
          - Enabled: false
      Tags: 
        - Key: "Purpose"
          Value: "Subordinate CA4"
  rSubordinateCA4Cert:
    Type: "AWS::ACMPCA::Certificate"
    Metadata:
      cfn-lint:
        config:
          ignore_checks:
            - W3005
    Condition: CreateSub4
    DependsOn: rSubordinateCA3Activation
    Properties:
      CertificateAuthorityArn: !Ref rSubordinateCA3Activation
      CertificateSigningRequest: !GetAtt rSubordinateCA4.CertificateSigningRequest
      SigningAlgorithm: !Ref pSigningAlgorithm
      TemplateArn: "arn:aws:acm-pca:::template/SubordinateCACertificate_PathLen0/V1"
      Validity:
        Type: DAYS
        Value: !Ref pSub4CACertExpiry
  rSubordinateCA4Activation:
    Type: "AWS::ACMPCA::CertificateAuthorityActivation"
    Condition: CreateSub4
    Properties:
      CertificateAuthorityArn: !Ref rSubordinateCA4
      Certificate: !GetAtt rSubordinateCA4Cert.Certificate
      CertificateChain: !GetAtt rSubordinateCA3Activation.CompleteCertificateChain
      Status: ACTIVE
  # Only turn this on if there are no subordinates, otherwise share out only lowest level subordinate
  rRootShare:
    Type: AWS::RAM::ResourceShare
    Condition: CreateRootShare
    DependsOn: rRootCAActivation
    Properties:
      AllowExternalPrincipals: false
      Name: !Sub
        - "RootCAResourceShare-${CommonName}"
        - CommonName: !Ref pRootCommonName
      Principals: !If
        - CreateOUShare
        - - !Sub "arn:aws:organizations::${pManagmentAccount}:ou/${pAWSOrganizationId}/${pAWSOrganizationalUnit}"
        - !If
          - CreateOrgShare
          - - !Sub "arn:aws:organizations::${pManagmentAccount}:organization/${pAWSOrganizationId}"
          - !Ref "AWS::NoValue"
      ResourceArns:
        - !Ref rRootCA
      Tags: 
        - Key: "Purpose"
          Value: "Private CA for certificate signing"
  rSubordinateCA1Share:
    Type: AWS::RAM::ResourceShare
    Condition: CreateSub1Share
    DependsOn: rSubordinateCA1Activation
    Properties:
      AllowExternalPrincipals: false
      Name: !Sub
        - "SubordinateCAResourceShare-${CommonName}"
        - CommonName: !Ref pSub1CommonName
      Principals: !If
        - CreateOUShare
        - - !Sub "arn:aws:organizations::${pManagmentAccount}:ou/${pAWSOrganizationId}/${pAWSOrganizationalUnit}"
        - !If
          - CreateOrgShare
          - - !Sub "arn:aws:organizations::${pManagmentAccount}:organization/${pAWSOrganizationId}"
          - !Ref "AWS::NoValue"
      ResourceArns:
        - !Ref rSubordinateCA1
      Tags: 
        - Key: "Purpose"
          Value: "Private CA for certificate signing"
  rSubordinateCA2Share:
    Type: AWS::RAM::ResourceShare
    Condition: CreateSub2Share
    DependsOn: rSubordinateCA2Activation
    Properties:
      AllowExternalPrincipals: false
      Name: !Sub
        - "SubordinateCAResourceShare-${CommonName}"
        - CommonName: !Ref pSub2CommonName
      Principals: !If
        - CreateOUShare
        - - !Sub "arn:aws:organizations::${pManagmentAccount}:ou/${pAWSOrganizationId}/${pAWSOrganizationalUnit}"
        - !If
          - CreateOrgShare
          - - !Sub "arn:aws:organizations::${pManagmentAccount}:organization/${pAWSOrganizationId}"
          - !Ref "AWS::NoValue"
      ResourceArns:
        - !Ref rSubordinateCA2
      Tags: 
        - Key: "Purpose"
          Value: "Private CA for certificate signing"
  rSubordinateCA3Share:
    Type: AWS::RAM::ResourceShare
    Condition: CreateSub3Share
    DependsOn: rSubordinateCA3Activation
    Properties:
      AllowExternalPrincipals: false
      Name: !Sub
        - "SubordinateCAResourceShare-${CommonName}"
        - CommonName: !Ref pSub3CommonName
      Principals: !If
        - CreateOUShare
        - - !Sub "arn:aws:organizations::${pManagmentAccount}:ou/${pAWSOrganizationId}/${pAWSOrganizationalUnit}"
        - !If
          - CreateOrgShare
          - - !Sub "arn:aws:organizations::${pManagmentAccount}:organization/${pAWSOrganizationId}"
          - !Ref "AWS::NoValue"
      ResourceArns:
        - !Ref rSubordinateCA3
      Tags: 
        - Key: "Purpose"
          Value: "Private CA for certificate signing"
  rSubordinateCA4Share:
    Type: AWS::RAM::ResourceShare
    Condition: CreateSub4Share
    DependsOn: rSubordinateCA4Activation
    Properties:
      AllowExternalPrincipals: false
      Name: !Sub
        - "SubordinateCAResourceShare-${CommonName}"
        - CommonName: !Ref pSub4CommonName
      Principals: !If
        - CreateOUShare
        - - !Sub "arn:aws:organizations::${pManagmentAccount}:ou/${pAWSOrganizationId}/${pAWSOrganizationalUnit}"
        - !If
          - CreateOrgShare
          - - !Sub "arn:aws:organizations::${pManagmentAccount}:organization/${pAWSOrganizationId}"
          - !Ref "AWS::NoValue"
      ResourceArns:
        - !Ref rSubordinateCA4
      Tags: 
        - Key: "Purpose"
          Value: "Private CA for certificate signing"
# output of stacks are buckets and end-entity subordinate
Outputs:
  oCRLBucket:
    Condition: EnableCRL
    Description: The CRL bucket
    Value: !Ref rRootCRLBucket
    Export:
      Name: !Sub "${AWS::StackName}-CRLBucket"
  oAccessLogBucket:
    Condition: EnableCRLLog
    Description: The Access Log bucket
    Value: !Ref rLogCRLBucket
    Export:
      Name: !Sub "${AWS::StackName}-AccessLogBucket"
  oRootCA:
    Description: The ARN of the Root Certificate Authority that can be used by other stacks
    Value: !Ref rRootCA
    Export:
      Name: !Sub "${AWS::StackName}-RootCA"  
  oCertAuthority:
    Condition: CreateShare
    Description: The ARN of the Certificate Authority for issuing certificate
    Value: !If
        - CreateSub4Share
        - rSubordinateCA3Activation.CompleteCertificateChain
        - !If
          - CreateSub3Share
          - !Ref rSubordinateCA3
          - !If
            - CreateSub2Share
            - !Ref rSubordinateCA2
            - !If
              - CreateSub1Share
              - !Ref rSubordinateCA1
              - !If
                - CreateRootShare
                - !Ref rRootCA
                - !Ref "AWS::NoValue"
    Export:
      Name: !Sub "${AWS::StackName}-CertAuthority"