Logo Teamwork

Fn::GetStackOutput — Référencer les outputs CloudFormation entre comptes AWS

Published on
Authors
Version audio
Chargement...
Fn::GetStackOutput — Référencer les outputs CloudFormation entre comptes AWS - Guide Sylvain BRUAS


Dans un article précédent sur le VPC partagé, nous avons mis en place une architecture où le compte réseau possède et gère le Amazon Virtual Private Cloud (VPC) — subnets, tables de routage, NAT Gateways, AWS Transit Gateway — et les autres comptes AWS (par équipe ou par projet) viennent y déployer leurs workloads en utilisant les subnets partagés via AWS Resource Access Manager (RAM).

Cette architecture est élégante du point de vue de la gouvernance, mais elle soulève immédiatement une question pratique : comment les templates AWS CloudFormation des équipes font-ils référence aux ressources du compte réseau ? Le VpcId, les SubnetId, les security groups de base — tout ça vit dans le compte réseau, dans une stack que l'équipe réseau maintient.

Jusqu'à très récemment, les options disponibles étaient toutes imparfaites : hard-coder les IDs (fragile), passer par AWS Systems Manager (SSM) Parameter Store (overhead opérationnel), ou utiliser Fn::ImportValue (limité au même compte et même région). AWS vient de changer la donne avec Fn::GetStackOutput, une nouvelle fonction intrinsèque qui résout exactement ce problème.


Le contexte : VPC partagé en multi-compte

Rappelons rapidement l'architecture cible décrite dans l'article sur le VPC partagé. L'organisation AWS est découpée en plusieurs comptes :

Architecture multi-comptes SharedVPC avec Fn::GetStackOutput

Le compte réseau déploie une stack NetworkStack qui contient toutes les ressources réseau et expose des Outputs (VpcId, SubnetIds, NatGatewayId…). Les comptes équipes veulent utiliser ces valeurs dans leurs propres stacks CloudFormation.


Le problème : référencer des ressources cross-compte avant Fn::GetStackOutput

Avant l'introduction de Fn::GetStackOutput, trois approches coexistaient, chacune avec ses limitations :

1. Hard-coding des IDs

La solution la plus simple et la plus dangereuse. On récupère les IDs manuellement et on les copie dans les templates ou dans des fichiers de paramètres.

# Avant — fragile et non reproductible
Parameters:
  VpcId:
    Type: String
    Default: "vpc-0abc123def456789"  # ← IDs en dur
  SubnetPrivateA:
    Type: String
    Default: "subnet-0123456789abcdef0"

Le problème : si le compte réseau recrée le VPC (migration, incident, refactoring), tous les templates des équipes doivent être mis à jour manuellement. C'est une bombe à retardement.

2. SSM Parameter Store cross-compte

L'équipe réseau stocke les valeurs dans SSM, et les équipes consomment via des SSM dynamic references.

# Stack réseau — écriture dans SSM
VpcIdParam:
  Type: AWS::SSM::Parameter
  Properties:
    Name: /network/vpc-id
    Value: !Ref MyVpc
# Stack équipe — lecture depuis SSM cross-compte (impossible nativement)
VpcId: "{{resolve:ssm:/network/vpc-id}}"  # ✗ ne fonctionne pas cross-compte

Les SSM dynamic references ne fonctionnent pas en cross-compte. L'équipe réseau devrait publier dans SSM dans chaque compte équipe, ce qui génère un overhead opérationnel considérable.

3. Fn::ImportValue — limité au même compte

Fn::ImportValue est la fonction native CloudFormation pour partager des outputs entre stacks. Elle crée une référence forte (le stack producteur ne peut pas être supprimé tant qu'une stack consommatrice existe), mais elle est strictement limitée au même compte et même région.

# ✗ Ne fonctionne pas cross-compte
VpcId: !ImportValue "NetworkStack-VpcId"

Aucune de ces approches ne résout proprement le cas du VPC partagé en multi-compte. C'est pour ça que Fn::GetStackOutput est une avancée significative.


Fn::GetStackOutput : la solution native

Fn::GetStackOutput est une fonction intrinsèque CloudFormation qui permet de référencer les outputs d'une autre stack, potentiellement dans un autre compte AWS ou une autre région. Elle résout la valeur au moment du déploiement (create ou update), en appelant l'API DescribeStacks de CloudFormation.

Syntaxe

La fonction accepte quatre paramètres :

ParamètreObligatoireDescription
StackNameOuiNom de la stack productrice
OutputNameOuiIdentifiant logique de l'output (pas le nom d'export)
RegionNonRégion AWS de la stack productrice
RoleArnNonARN du rôle IAM à assumer pour l'accès cross-compte

Comment CloudFormation résout la valeur

Flux de résolution de Fn::GetStackOutput en 5 étapes

Le mécanisme est simple : au moment du déploiement, CloudFormation assume le rôle AWS Identity and Access Management (IAM) spécifié dans le compte réseau, appelle cloudformation:DescribeStacks sur la NetworkStack, récupère la valeur de l'output demandé, et l'injecte dans le template en cours de déploiement.


Mise en pratique : templates CloudFormation

Stack productrice — compte réseau

L'équipe réseau déclare ses outputs normalement. Pas besoin de Export — c'est une différence importante avec Fn::ImportValue qui requiert un nom d'export.

# NetworkStack — compte réseau (111111111111)
AWSTemplateFormatVersion: '2010-09-09'
Description: Infrastructure réseau partagée

Resources:
  SharedVpc:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: "10.0.0.0/16"
      EnableDnsHostnames: true
      EnableDnsSupport: true
      Tags:
        - Key: Name
          Value: shared-vpc-production

  SubnetPrivateA:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref SharedVpc
      CidrBlock: "10.0.1.0/24"
      AvailabilityZone: !Select [0, !GetAZs ""]

  SubnetPrivateB:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref SharedVpc
      CidrBlock: "10.0.2.0/24"
      AvailabilityZone: !Select [1, !GetAZs ""]

  SubnetPrivateC:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref SharedVpc
      CidrBlock: "10.0.3.0/24"
      AvailabilityZone: !Select [2, !GetAZs ""]

Outputs:
  VpcId:
    Description: "ID du VPC partagé"
    Value: !Ref SharedVpc

  SubnetPrivateA:
    Description: "Subnet privé — AZ A"
    Value: !Ref SubnetPrivateA

  SubnetPrivateB:
    Description: "Subnet privé — AZ B"
    Value: !Ref SubnetPrivateB

  SubnetPrivateC:
    Description: "Subnet privé — AZ C"
    Value: !Ref SubnetPrivateC

Notez l'absence de bloc Export dans les outputs — Fn::GetStackOutput référence directement le nom logique de l'output (VpcId, SubnetPrivateA…), pas un nom d'export.

Stack consommatrice — compte équipe

# AppStack — compte équipe A (222222222222)
AWSTemplateFormatVersion: '2010-09-09'
Description: Application déployée dans le VPC partagé

Parameters:
  NetworkAccountId:
    Type: String
    Default: "111111111111"

Resources:
  AppSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: "SG pour l'application"
      VpcId:
        Fn::GetStackOutput:
          StackName: NetworkStack
          OutputName: VpcId
          RoleArn: !Sub "arn:aws:iam::${NetworkAccountId}:role/GetStackOutputRole"

  AppLoadBalancer:
    Type: AWS::ElasticLoadBalancingV2::LoadBalancer
    Properties:
      Type: application
      Subnets:
        - Fn::GetStackOutput:
            StackName: NetworkStack
            OutputName: SubnetPrivateA
            RoleArn: !Sub "arn:aws:iam::${NetworkAccountId}:role/GetStackOutputRole"
        - Fn::GetStackOutput:
            StackName: NetworkStack
            OutputName: SubnetPrivateB
            RoleArn: !Sub "arn:aws:iam::${NetworkAccountId}:role/GetStackOutputRole"
        - Fn::GetStackOutput:
            StackName: NetworkStack
            OutputName: SubnetPrivateC
            RoleArn: !Sub "arn:aws:iam::${NetworkAccountId}:role/GetStackOutputRole"
      SecurityGroups:
        - !Ref AppSecurityGroup

La syntaxe est identique pour chaque référence cross-compte. L'OutputName est exactement le nom déclaré dans le bloc Outputs de la stack réseau.


Configuration IAM : le rôle GetStackOutputRole

C'est la pièce centrale de la sécurité du dispositif. L'équipe réseau crée un rôle IAM dans son compte, que CloudFormation (depuis les comptes équipes) peut assumer pour lire les outputs.

Configuration IAM — GetStackOutputRole et trust policy cross-compte

Voici le template CloudFormation pour créer le rôle IAM dans le compte réseau :

# À déployer dans le compte réseau — peut faire partie de NetworkStack
GetStackOutputRole:
  Type: AWS::IAM::Role
  Properties:
    RoleName: GetStackOutputRole
    AssumeRolePolicyDocument:
      Version: "2012-10-17"
      Statement:
        - Effect: Allow
          Principal:
            AWS:
              - "arn:aws:iam::222222222222:root"  # Compte Équipe A
              - "arn:aws:iam::333333333333:root"  # Compte Équipe B
          Action: sts:AssumeRole
    Policies:
      - PolicyName: DescribeNetworkStack
        PolicyDocument:
          Version: "2012-10-17"
          Statement:
            - Effect: Allow
              Action:
                - cloudformation:DescribeStacks
              Resource:
                - !Sub "arn:aws:cloudformation:${AWS::Region}:${AWS::AccountId}:stack/NetworkStack/*"

Points importants sur ce rôle :

  • La permission cloudformation:DescribeStacks est suffisante — pas besoin d'accès aux ressources réseau elles-mêmes
  • Le Resource est restreint à NetworkStack uniquement, pas à toutes les stacks du compte réseau
  • La trust policy liste explicitement les comptes autorisés — on peut affiner avec un Principal par rôle d'exécution CloudFormation plutôt que par compte (:root)

Référence forte vs référence faible

C'est la différence fondamentale entre Fn::ImportValue et Fn::GetStackOutput :

CaractéristiqueFn::ImportValueFn::GetStackOutput
PortéeMême compte + même régionCross-compte, cross-région
Nécessite un ExportOuiNon
Type de référenceForteFaible
Suppression du stack producteurBloquéeAutorisée
Propagation automatique des changementsNonNon

Une référence faible signifie que si l'équipe réseau supprime ou recrée la NetworkStack, CloudFormation ne l'en empêchera pas. Les stacks consommatrices ne le sauront pas immédiatement — elles échoueront uniquement lors de leur prochain déploiement.

Pour compenser, deux mesures sont recommandées :

  1. Activer la protection contre la suppression sur NetworkStack :
aws cloudformation update-termination-protection \
  --enable-termination-protection \
  --stack-name NetworkStack \
  --region eu-west-1
  1. Documenter les dépendances dans les READMEs et les runbooks — les références Fn::GetStackOutput n'apparaissent pas dans la console CloudFormation du compte réseau.

Intégration avec AWS CDK

AWS Cloud Development Kit (CDK) supporte nativement Fn::GetStackOutput pour les références cross-compte et cross-région. Avec la clé de contexte @aws-cdk/core:defaultCrossStackReferences :

  • strong (défaut) : utilise Fn::ImportValue pour les références dans le même compte/région
  • weak : utilise toujours Fn::GetStackOutput
  • both : génère les deux types simultanément (migration progressive)

Pour notre architecture SharedVPC, le comportement weak est le bon choix : on est obligatoirement en cross-compte.

// cdk.json
{
  "context": {
    "@aws-cdk/core:defaultCrossStackReferences": "weak"
  }
}

Ce qu'il faut retenir

  • Fn::GetStackOutput permet de référencer les outputs d'une stack CloudFormation dans n'importe quel compte AWS et n'importe quelle région, sans avoir besoin d'un nom d'export
  • Pour le pattern VPC partagé, c'est la solution idéale : l'équipe réseau expose ses outputs (VpcId, SubnetIds…), les équipes applicatives les consomment directement depuis leurs stacks
  • La résolution se fait au moment du déploiement via cloudformation:DescribeStacks — un rôle IAM dans le compte réseau est nécessaire pour les accès cross-compte
  • La référence est faible (pas de blocage de suppression) — compensez avec la protection contre la suppression sur la NetworkStack et une documentation explicite des dépendances
  • La valeur est figée au moment du déploiement. Si l'équipe réseau modifie un output, les stacks consommatrices doivent être mises à jour explicitement pour récupérer la nouvelle valeur

Fn::GetStackOutput ne remplace pas Fn::ImportValue pour les cas dans le même compte — la référence forte garde tout son intérêt quand on veut une garantie d'intégrité référentielle. Mais pour le cross-compte, c'est désormais l'option native et la plus propre.

Partager cet article