Je cherchais la même fonctionnalité. L'utilisation d'une pile imbriquée comme SpoonMeiser l'a suggéré m'est venue à l'esprit, mais j'ai réalisé que ce dont j'avais réellement besoin, c'était de fonctions personnalisées. Heureusement, CloudFormation permet d'utiliser AWS :: CloudFormation :: CustomResource qui, avec un peu de travail, permet de faire exactement cela. Cela semble exagéré pour les seules variables (quelque chose que je dirais qui aurait dû être dans CloudFormation en premier lieu), mais il fait le travail et, en plus, permet toute la flexibilité de (faites votre choix de python / nœud /Java). Il convient de noter que les fonctions lambda coûtent de l'argent, mais nous parlons ici de quelques centimes, sauf si vous créez / supprimez vos piles plusieurs fois par heure.
La première étape consiste à créer une fonction lambda sur cette page qui ne fait que prendre la valeur d'entrée et la copier dans la sortie. Nous pourrions avoir la fonction lambda faire toutes sortes de choses folles, mais une fois que nous avons la fonction d'identité, tout le reste est facile. Alternativement, nous pourrions avoir la fonction lambda en cours de création dans la pile elle-même. Étant donné que j'utilise plusieurs piles dans un compte, j'aurais tout un tas de fonctions et de rôles lambda restants (et toutes les piles doivent être créées avec --capabilities=CAPABILITY_IAM
, car elles ont également besoin d'un rôle.
Créer une fonction lambda
- Accédez à la page d'accueil de lambda et sélectionnez votre région préférée
- Sélectionnez "Fonction vierge" comme modèle
- Cliquez sur "Suivant" (ne configurez aucun déclencheur)
- Remplir:
- Nom: CloudFormationIdentity
- Description: retourne ce qu'il obtient, prise en charge variable dans Cloud Formation
- Runtime: python2.7
- Type d'entrée de code: Modifier le code en ligne
- Code: voir ci-dessous
- Gestionnaire:
index.handler
- Rôle: créez un rôle personnalisé. À ce stade, une fenêtre contextuelle s'ouvre qui vous permet de créer un nouveau rôle. Acceptez tout sur cette page et cliquez sur "Autoriser". Il créera un rôle avec des autorisations pour publier dans les journaux cloudwatch.
- Mémoire: 128 (c'est le minimum)
- Délai d'attente: 3 secondes (devrait être suffisant)
- VPC: aucun VPC
Copiez-collez ensuite le code ci-dessous dans le champ de code. Le haut de la fonction est le code du module python cfn-response , qui n'est installé automatiquement que si la fonction lambda est créée via CloudFormation, pour une raison étrange. La handler
fonction est assez explicite.
from __future__ import print_function
import json
try:
from urllib2 import HTTPError, build_opener, HTTPHandler, Request
except ImportError:
from urllib.error import HTTPError
from urllib.request import build_opener, HTTPHandler, Request
SUCCESS = "SUCCESS"
FAILED = "FAILED"
def send(event, context, response_status, reason=None, response_data=None, physical_resource_id=None):
response_data = response_data or {}
response_body = json.dumps(
{
'Status': response_status,
'Reason': reason or "See the details in CloudWatch Log Stream: " + context.log_stream_name,
'PhysicalResourceId': physical_resource_id or context.log_stream_name,
'StackId': event['StackId'],
'RequestId': event['RequestId'],
'LogicalResourceId': event['LogicalResourceId'],
'Data': response_data
}
)
if event["ResponseURL"] == "http://pre-signed-S3-url-for-response":
print("Would send back the following values to Cloud Formation:")
print(response_data)
return
opener = build_opener(HTTPHandler)
request = Request(event['ResponseURL'], data=response_body)
request.add_header('Content-Type', '')
request.add_header('Content-Length', len(response_body))
request.get_method = lambda: 'PUT'
try:
response = opener.open(request)
print("Status code: {}".format(response.getcode()))
print("Status message: {}".format(response.msg))
return True
except HTTPError as exc:
print("Failed executing HTTP request: {}".format(exc.code))
return False
def handler(event, context):
responseData = event['ResourceProperties']
send(event, context, SUCCESS, None, responseData, "CustomResourcePhysicalID")
- Cliquez sur Suivant"
- Cliquez sur "Créer une fonction"
Vous pouvez maintenant tester la fonction lambda en sélectionnant le bouton "Test" et sélectionnez "CloudFormation Create Request" comme exemple de modèle. Vous devriez voir dans votre journal que les variables qui y sont alimentées sont retournées.
Utiliser une variable dans votre modèle CloudFormation
Maintenant que nous avons cette fonction lambda, nous pouvons l'utiliser dans les modèles CloudFormation. Prenez d'abord note de la fonction lambda Arn (allez sur la page d'accueil lambda , cliquez sur la fonction qui vient d'être créée, l'Arn devrait être en haut à droite, quelque chose comme arn:aws:lambda:region:12345:function:CloudFormationIdentity
).
Maintenant, dans votre modèle, dans la section des ressources, spécifiez vos variables comme:
Identity:
Type: "Custom::Variable"
Properties:
ServiceToken: "arn:aws:lambda:region:12345:function:CloudFormationIdentity"
Arn: "arn:aws:lambda:region:12345:function:CloudFormationIdentity"
ClientBucketVar:
Type: "Custom::Variable"
Properties:
ServiceToken: !GetAtt [Identity, Arn]
Name: !Join ["-", [my-client-bucket, !Ref ClientName]]
Arn: !Join [":", [arn, aws, s3, "", "", !Join ["-", [my-client-bucket, !Ref ClientName]]]]
ClientBackupBucketVar:
Type: "Custom::Variable"
Properties:
ServiceToken: !GetAtt [Identity, Arn]
Name: !Join ["-", [my-client-bucket, !Ref ClientName, backup]]
Arn: !Join [":", [arn, aws, s3, "", "", !Join ["-", [my-client-bucket, !Ref ClientName, backup]]]]
Je spécifie d'abord une Identity
variable qui contient l'Arn pour la fonction lambda. Mettre ceci dans une variable ici, signifie que je n'ai qu'à le spécifier une fois. Je fais toutes mes variables de type Custom::Variable
. CloudFormation vous permet d'utiliser n'importe quel nom de type commençant par Custom::
pour des ressources personnalisées.
Notez que la Identity
variable contient deux fois Arn pour la fonction lambda. Une fois pour spécifier la fonction lambda à utiliser. La deuxième fois comme valeur de la variable.
Maintenant que j'ai la Identity
variable, je peux définir de nouvelles variables en utilisant ServiceToken: !GetAtt [Identity, Arn]
(je pense que le code JSON devrait être quelque chose comme "ServiceToken": {"Fn::GetAtt": ["Identity", "Arn"]}
). Je crée 2 nouvelles variables, chacune avec 2 champs: Name et Arn. Dans le reste de mon modèle, je peux l'utiliser !GetAtt [ClientBucketVar, Name]
ou !GetAtt [ClientBucketVar, Arn]
quand j'en ai besoin.
Un mot d'avertissement
Lorsque vous travaillez avec des ressources personnalisées, si la fonction lambda se bloque, vous êtes bloqué entre 1 et 2 heures, car CloudFormation attend une réponse de la fonction (bloquée) pendant une heure avant d'abandonner. Par conséquent, il peut être utile de spécifier un court délai d'attente pour la pile lors du développement de votre fonction lambda.